I'm using ngView's to load content on demand.
What I don't quite get is when is the controller for each view created?
I have the same controller assigned to a few of my views, and they seem to share data between them. yet my impression is that the controller is recreated each time the specific view is shown.
So how does it work?
When you define a controller in your JavaScript using .controller(), you defining a class, not creating an instance of an object.
Colin Moock defines the difference this way: an Object is like an airplane, it can perform actions like fly() and has properties like maxPassengers. A Class is like the blueprint on how to build that airplane, from which you can make infinite airplanes. Amazing! You can watch Colin Moock's whole explanation right here if you want: http://tv.adobe.com/watch/colin-moocks-lost-actionscript-weekend/object-oriented-programming-overview/
Every time the view is loaded, a new Instance of your controller Class is created. Most Angular.js controllers have a capitalized name like Dashboard or Contacts because they describe a Class.
So the answer to your question is a new instance of your controller is created every time your view loads.
It turns out that when using a factory for your model and injecting the model into the controller, then you can keep state when you change views.
I used a factory for one of my controllers and "new FooModel()" inside another controller, thus two different behaviors on the views using the two controllers.
Related
This is somewhat a follow up on my "Is it bad practice for an angular directive to request data" Q.
My Q Is where would be the appropriate place to keep application data?
for example - information about the current user such as his name and his roles in the app?
differrent areas (on the screen) and components will depend on this data (e.g - side bar will want to know if the curentUser.isAnAdmin and a helloUser Directive would like to know the currentUser.name
Does this mean that the currentUser should be placed on the $rootScope?
and what should be the trigger for retrieving the initial data for the currentUser and for refreshing this information?
I was thinking of having several ngControllers responsible for setting up this data on the scope of the same html node as that of the ngApp, but found out that it is not possible to have multiple ngControllers on a single HTML Item.
I am now thinking of having multiple services with methods that get a scope object and assign the data they are responsible to onto that scope.
It would allow me to separate code for currentUser from code for someOtherSharedAppData into two different services and call both of them from the applications's main controller thus assiging the data to the scope associated with the top-most element in the app - does that make sense?
In fact you asked two questions here:
Where to store and manipulate data?
When and how should I use the $rootScope (compared to $scope)?
1)
I will refer to this article:
Whenever data and methods need to be reusable I would write a service like this for example.
angular.module('modelDemo').service("myModel", [function() {
this.list = [what, ever, items, you, have];
this.property= null;
this.setProperty = function(value) {
this.property= value;
};
}]);
Note, that I'm not passing the $scope as you considered. Instead I would inject the service in my controller and bind the $scope variables like this:
$scope.list = myModel.list;
If you need, you can even bind to the full model
$scope.myModel = myModel;
myModel.setPropery(value)
Got the idea? This way all model changes will be directly available to the corresponging view
{{myModel.property}}
ng-repeat="item in myModel.list"
ng-click="myModel.setProperty(item)"
Conclusion: Yes, you should have different services for your user model and your someOtherSharedAppData models.
2)
I will refer to this SO Question.
In short: If you have data that should be available in many views, it is OK to bind your (service) model to $rootScope variables. As you can see in the mentioned discussion there are also other opinions but I think the conclusion is: It depends on the structure and needs of your application.
I'm fairly new to angular, after using a video tutorial and reading some documentation, I decided to rebuild an old app of mine as an example with angularjs.
So this app has a table showing some data. It has a form underneath which helps you modify the data from the list. You have a button on each line which allows you to edit the line, it then fill all the fields in the form and you can then save or cancel your changes.
I made a controller to handle the list, it works fine, it gets a json from http.
I used ng-click on my edit button to trigger a function in this controller, giving it the whole object it's supposed to edit.
I made a controller to handle the form in which the edit should take place and I don't really found a 'non-hacky' way to pass the data from the list controller to the form controller.
So, my question is : what is the best practice and/or the common way to get this data from my list controller to my form controler ?
It depends how you are using the form controller. If it's being used within a template using ng-controller attribute, then this controller has access to parent scope, so you can work with list controller's data. (although note some scope-inheritance quirks solved by "dot notation", explained nicely by egghead.io: https://egghead.io/lessons/angularjs-the-dot)
If you're launching your edit form in a another url (e.g. /items/2/edit) and handle it in a routing configuration, then you can use resolve property to pass any data to the controller: $routeProvider docs.
You can pass the entire object as parameter
<row ng-repeat="theModelInRow in modelList" ng-click="edit(theModelInRow)">
if form controller isn't a nested controller of list controller on view, then you can use rootScope.
I'm confused by these three different ways to specify a controller.
1- I can have an include in the app/index.html file:
<script src="scripts/controller/nav.js"></script>
2- I can have an attribute in a route:
.when('/link/list/', {
templateUrl: 'view/list.html',
controller: 'navController'
})
3- I can have an attribute in a view:
ng-controller="navController"
It's quite a few. I wonder which way to go and when.
Kind Regards,
Stephane Eybert
Your (1) has nothing to do with (2) and (3).
And there are other places where you can bind controllers (e.g. a directive's controller property).
Each way is serves a different purpose, so go with the one that suits your situation.
If you have a directive and want to give it a specific controller, use the Directive Definition Object's controller property.
If you use ngView and want to give each view a specific controller (as is usually the case) use the $routeProviders controller.
If you want to assign a controller to some part of your view (in the main file or in a view or partial) use ngController.
All the above are methods for "binding" a controller to some part of the view (be it a single element or the whole HTML page or anything in between).
Im quite new too but Ill try to explain in a more layman way.
1 For each .js file you have (which may contain one or more controller defined), you need a corresponding entry into the script for #1 there. Its not the controller itself, more like to allow script to recognise that this .js file is part of the set of files to run.
2 is more like specify a state or route, which may or may not use a controller. Its much like saying how one event should lead to another. The controller may be involved in the transitions of the states/routes (ie. responsible from one state, to another) or within a view itself.
3 is for using a controller's functions within a view itself.
I've added comments to one of the answers, but aside from syntax this may is more of a design question. Here is my opinion
Firstly, (1) is irrelevant to the conversation.
(2) is the preferred approach when specifying the controller for the view as it decouples the controller from the view itself. This can be useful when you want to re-use the same view by providing a different controller.
If you find yourself using (3),consider making that area into a directive, since by specifying a controller you are indicating that it requires its own logic.
Here my app.js code :
Ext.Loader.setConfig({
enabled: true
});
Ext.application({
name: 'KP',
appFolder: 'scripts/app',
controllers: [
'login.Login'
],
views: [
'login.Login'
],
launch: function () {
var view = Ext.widget('login')
}
});
If I want to use some others views, controllers, models and stores in my application, should I define them in app.js? (like this : controllers[....all of my controllers.....])
Are there other way to get to work init function in my controllers? Thanks!
There are many ways...
Following some basics first:
All controllers that are listed within the controllers array of the application controller get instantiated at startup (application get initialized with the onReady event. Also the init() and onLaunch() methods of the listed controllers get called. See the linked API for details when this occurs). Now each instantiated controller initialize its stores,views and models (creates getter for all and over that creates instances of each store while overwriting the storeId and append them to the Ext.StoreMgr). Note that each controller will contains his own models, stores and views.
The simplest way to receive a controller that is not listed in the application controller is by using a reference of the application controller and calling getController(name)
It is Important to know that while using that way the receive a controller instance the getter method will not call the init() or onLaunch() method for the invoked controller, it will just try to get the controller or create it. You should also note the API for those methods in these controllers are no longer correct. You need to set an init bool to these controllers and check it on the reference that the getController(name) method hands back to you. If it is undefined/false you call the init() / onLaunch() by yourself and set it after that. I use these technique by myself to reduce intial load for bigger application. I guess you don't really need this for just a handful of controllers.
Update
Due to some changes in the 4.1.x release it is no longer required to init the controller
manually. This is now done for us by the getController method
You can define as many controllers as you want. To implement hierarchy you can define views and stores related to certain controller within it.
For example:
controller/
artists.js (inside: artistsView1.js, artistsView2.js, artistsStore.js)
paintings.js (inside: paintingsView1.js, paintingsStore.js)
All of your views and stores used in controllers would be loaded and initializadduring Applocation load.
The controllers you define in your application are loaded before the application launch() is called. As each controller is loaded its models, views and stores (MVS) are also loaded.
Although you can define the MVSs in your application, I'd recommend that each controller defines its related MVSs, as it is possible for the programmer to load controllers (and their related MVSs) upon request rather than by default. It is also a good practice from reusability point of view (so if you ever to reuse the controller, you know what MVSs come with it).
You may want to include some views in the application if, say, they are used without controllers, like some sort of a custom window you display if there was some error in the system.
I need to override & add methods in a model invoked by a controller. I don't want to write anything inside Model class file because it's used by other controllers too. Methods are like pagination() and find(), can I do it from inside a controller?
CakePHP behaviors are mixins. They add methods to a model, which is what you are looking for.
It sounds like dynamically attaching a behavior to the model would get you the outcome you need.
Looking at Model::__construct(), I can see that it calls $this->Behaviors->init($this->alias, $this->actsAs);.
You may be able to call it again after the model has been instantiated to attach different behaviors (ie. $this->MyModel->Behaviors->init('MyModel', array('MyBehavior'));).
In fact, a closer look reveals that $this->MyModel->Behaviors is an instance of BehaviorCollection. As such, you can use the attach() method.