Im wondering how to structure nested controllers in Marionette, I have this hierarchy
Menu (module)
MenuController
MenuItemCollectionView
MenuItemView
MenuItemDropdownController
MenuItemDropdownLayout
MenuItemDropdownSidebarView
MenuItemDropdownContentView
Basicly im building a navigation menu similar to polygon.com (when you hover over items a "dropdown" appear).
I looked alot after some examples of "nested controllers" in Marionette but without luck, my understanding is i need to separate out a Controller for each Dropdown, and maybe aslo a controller for each MenuItemView. The way ive seen examples of something similar is that the "High level controller" is responsible for everything. Maybe my understanding of controllers in Marionette is wrong, and they are more intented for Routing purpose.
How would you guys structure this would you have the MenuItemView be responsible for creating a MenuItemDropdownController? (which i find abit odd.)
Or maybe im thinking about this the wrong way maybe this should be separated into multiple modules? but i cant really wrap that around in my head, as i see modules as single items like a "Menu" / "Login form" etc and not items in a collection view.
You should have one single controller to handle your menu.
This case should be manageable using composite views, and I wrote a blog post with a similar objective here: http://davidsulc.com/blog/2013/02/03/tutorial-nested-views-using-backbone-marionettes-compositeview/
The complexity of your various subviews could also be handled by using Marionette layouts.
You can see an example of a controller handling a layout here (see contactsListLayout): https://github.com/davidsulc/marionette-gentle-introduction/blob/master/assets/js/apps/contacts/list/list_controller.js#L43
Basically, a layout is "a view containing subviews in their own regions".
Handling nested layouts is explained in more detail in the book that builds the above app (available here).
Neither backbone nor Marionette have strong opinion on how a controller should be used beyond routing.
In practice controllers and routes are used to put applications into a specific state, in your example I think you are overusing controllers. In your case a single controller should be enough to handle your menu.
Related
I searched for a lot of design patterns, but found nothing on how to implement functions that are going to be used both in some controllers and in html markup. I really want to escape copy-pasting functionality, so here goes the problem.
I have some items stored in localStorage as an Array of entitites, each of which has a price, quantity and discount. Call that array a cart of items. In a certain page I need to interpolate the sum of the prices as an "overall price". That is the 'cart displaying page', which is basically a list of items from the cart. On the other hand, I have an 'order page' with a form, in which the overall price should also be shown. Say this pages are manipulated with two different controllers: OrderController and CartController, in both of which I have a function called calculatePrice, which returns the overall sum, which is then interpolated inside the html in both order.html and cart.html.
So it happens I already did some copy-pasting. And if (which is possible in the nearest future) I have another controller/view which needs this function, I'll end up copy-pasting again.
My thoughts on this:
1) Create a $rootScope function calculatePrice and just use it as is, both inside controllers and the view. Undesired, don't want to insert too much imperative code inside the 'run' cycle of my app
2) Create a service for utility functions, inject it inside the controllers and only declarative style like '$scope.someFunction = service.someFunction'
3) Create a service, inject it inside the app.run function and connect it to rootScope
But I don't think any of this is right. I want to find the most possible clean solution.
I think you can create a factory since it's instantiated only once and put your functions there and inject it in a controller when you need it and assign the desired function to variable in your $scope object since any html view should be linked to a controller. and i highly recommend that you follow some angularjs style guide rules for some famous developers, try this one i believe it's the best.
style guild for angularjs 1 &
style guide for angularjs 2
I have a question about the best way to structure the flow of an app using Angular 1.3. I'm trying to incorporate the 'Component-based Directive' approach as per guys like Matias Niemela (https://www.airpair.com/angularjs/workshops/component-based-directives-angularjs). He says in a video to still use controllers for routing rather than going completely controller-free, but to use directives to create reusable components that have all their dependencies injected.
I'm a bit new to Angular but have used Java mvc frameworks before where a controller written in Java first finishes getting the data needed on the page to be displayed, before some html is constructed and served up. I'm wondering if I'm coming a bit unstuck due to the asynchronous nature of the javascript calls used in Angular, maybe assuming things happen in an order they can't be guaranteed to....
I want to try to make the directives involved have anything they rely on injected (likely via attr's) for increased test-ability etc.
I had envisaged something like the following, for a simple customer listing, with clicking on a single customer in that list going through to a customer details page:
1) a route is defined with a target html page and a controller.
2) The controller gets the list of customers (ideally via a service), which is then available to the html page via Controller As (so Controller MyController As myCont, means the list should be available via something like myCont.myCustomers
3) A Directive is defined to display a customer row's details (name/address, plus a clickable link that passes the customer number as a parameter to a details page)
4) The html page has an ng-repeat to display all of myCustomer in myCustomers, using the Directive. So, I would want to pass the single customer details into the directive via Attr's (for my dependency injection) and then get them displayed for each customer...
(Alternatively, of course, the directive could take in all the customers and display all of them)
So, my question is really whether this is the correct approach to take, or am I missing something about the way these apps need to flow? I've tried unsuccessfully to get it working (posting this from home and my attempt is at work... I'll post the attempt if ppl think it valuable, but really my first question is about whether my overall approach is inherently flawed or not)
And then, secondly, to get the data for each customer passed to the directive, I'm assuming I need to pass the fields in in the html page that has the directive in it, passing something like {{ myCustomer }}... is this correct? (can I assume the Controller will have finished getting all the data before the ng-Repeat tries to cycle through all my customers and send their data to the directive, or not, in which case should I be passing the data via some other mechanism?)
I can't seem to find any examples on the web that tie together Component based Directives, with routing via a Controller, passing data from that controller to the directive, so any help is greatly appreciated!
Thanks!!
I've seen (and read) a lot of information on how nested scopes work and I seem to have understood it well and use/abuse it a lot. My question is not about how it's working, it's just a question of how to best use it. Let me give an example.
<body ng-controller="MainCtrl">
<div class="container" ng-view>
The view will load something like this:
<div ng-controller="CreateEventCtrl">
...
<div class="panel-body" ng-include src="'partials/eventinfo.html'">
That partial will also have a controller. This all makes great sense to me. I got a MainCtrl that know about your login information, you preferences etc. the CreateEventCtrl will know what is needed to handle creating, editing etc of the event and the specific partial (part of an accordian will need to display some specific information.
I might need to access stuff from one of the parent controllers, which is easy but my problem is; it can be done in so many different ways that I can't seem to figure out what the best way is.
So far I've tried the following implementations.
Have MainCtrl create a $scope.model that holds basic information and let all other child controllers just extend their own properties onto that model.
Have MainCtrl create a $scope.model and let child controllers append an extra model like this $scope.model.createEventModel = { ... } or just add the model directly on the scope like this $scope.createEventModel = { ... }
Have MainCtrl create a $scope.shared model and let the first level childcontrollers create a $scope.model that its next child can either extend, overwrite or add a new $scope.eventInfoModel on it, depending on what is best for the current view. All child controllers can opt to extend/overwrite properties on $scope.shared as well
It seems there are tons of variations and I just wonder if there is a best practice on this. I've tried google it but end up with a ton of posts trying to explain 'the dot', prototypal inheritance and how nested scopes work.
At the moment I'm going with nr.3 but I also know I will probably challenge my own decision at some point and rework it again. I would really want to shortcut all that extra work and learn what other people are doing.
UPDATE: I read everywhere that i should use Services to share data. But I don't get it. The data i want to share might be fetched through a Service but it's a one-way "get/fetch" operation. Also some of the data is user choices that reflect/filter the rest of the data
I listened to you guys and tried it out. I ended up creating a few services that holds my shared data and then have all controllers needing that information, have it injected.
While i really didn't like this approach to begin with, I find that it makes it clear what controllers will be using the data and what controllers will not. It also kind of helps with separation of concern as i can put all the store/load/modify/cache into the service and not worry about it in the controllers. All i need to do in the controllers is either get the data or put it back.
Been reading the Angular.js' Controller docs and stumbled across:
Sharing stateless or stateful code across Controllers — Use angular
services instead.
But this leaves me uncertain. How do one can share a stateless/stateful code between controllers? Or what does the "code" mean here? A model? Besides, controllers do not refer to each other, as far as I understood. Can anyone clear things out for me(others) please? Thanks.
I think what they are referring to might be one of the methods to "persist" data, sharing it between controllers or between route-changes. One way to do that is to put it in your rootScope, another is to use a service. If you define a service like this:
.factory("MyDataObject", function() {
return {};
})
Then MyDataObject will be the same object anywhere you call it, allowing you to save things into it in order to share data, functions and states between controllers (or directives, or other services, etc).
You never know with the Angular documentation, but I would guess that is what they are talking about :)
See for example this answer: Angularjs, passing scope between routes
Here is my view on subject. As angular guys have always tried to explain, scope is not your model. Angular "services" are way to do it, but word service is such and overloaded term. Coming from DDD background, I cannot reconcile word service with a state or statefulness, it just does not make sense to me. What makes more sense is ViewModel or whatever you want to call it. Since I've worked with Silverlight using MVVM pattern, I call them ViewModel. As it is a job of a "Controller" to provide Scope for a View, my controllers have been so far very lean. Bulk of logic is in a ViewModels that get associated with a View through a $scope that controller creates. Does that make sense? So my controller might take a dependency of let's say mySearchViewModel, bulk of the logic is in there and can be shared between controllers, and to associate it with a view you would do something like $scope.vm = mySearchViewModel in mySearchController.
I just started to use AngularJS, so I'm not an expert.
I have a div that represent the right area of my html view. In that div I have a controller, i.e.
<div class="rightContainer" ng-controller="rightContainerCtrl">...</div>
Inside that div I have a table, a search region, etc. Each region inside that div has its own controllers, it looks like this:
<div class="rightContainer" ng-controller="rightContainerCtrl">
...
<div class="search" ng-controller="searchCtrl">...</div>
...
<div class="table" ng-controller="tableCtrl">...</div>
</div>
the search region for example has its own controller and it is a child of rightContainerCtrl because it needs to alter some content in the parent (rightContainerCtrl), but the rightContainer div is growing and now it's big, and contain several nested controllers.
I think that using this nested controllers it's bad in this context because all the nested controllers share the parent scope, and not all controllers needs to access all the parent scope variables, also all the controllers are "prisoners" of the rightContainerCtrl, so they are highly coupled with their parent controller.
It looks like a God object anti-pattern (God controller in this case), so I think that instead of using nested controllers I can refactor my code to eliminate the rightContainerCtrl controller and use a service instead (like in a facade design pattern), that service then will be used by the controllers instead of sharing scope variables.
but since I'm not an AngularJs expert I'm not sure if I'm right or if it's better to leave this parent controller, maybe I'm missing something, so my question is
When is better to use nested controllers (nested scopes) and when it's better to use services instead in angularjs?
Controller/scope hierarchy should not dictate how data/models are shared in an application. When you think of data sharing in Angular, think dependency injection.
In the video that is referenced in #shaunhusain's answser, Misko states that scope should refer to the model, not be the model -- so don't model/put your data into scopes. Your models/data should normally be in a service.
When writing an Angular app, first think about your models. Put them in services with APIs to get/edit/manipulate the models. Then design your views. Each view should project/use/manipulate some subset of your models. Define a controller for each view that simply glues the needed subset of the models to the view. Make your controllers as thin as possible.
(Naming a controller rightContainerCtrl is also not recommended. The controller should not know about presentation/layout.)
This is 100% judgement call, and should be based on a couple of points.
Using events creates extremely loosely coupled components, they literally do not need to be aware of one another, if you have a situation where some parent controller would alleviate the need to communicate between a bunch of controllers (via services) then it is probably a better solution.
However if you're okay with the controllers each depending on the service (not really a problem) then you could just use the service as a means of communicating changes between the controllers. I've seen tons of arguments against the singleton (of which the service is a flavor, an injected singleton, but singleton nonetheless) I find these arguments to mostly be moot and generally lack a truly elegant and concise solution. If an argument spouts on and on about how when you go from A - D you don't want to take road B but they never seem to offer road C I don't really see the point.
http://www.youtube.com/watch?v=ZhfUv0spHCY&t=30m34s
I couldn't find the exact point in the video, but somewhere at the end here he discusses the usage of controllers vs services (he also reviews bi-directional data binding which will stop you from needing to pollute the global scope so to speak).