Hi i am having the 5 pages with 5 controllers and i am using the one service using injection in every controller . Is it possible to do without editing a code in views and controllers to stop the services functionality by writing code anywhere once in the any of one view or controller ?
For example I am having a app which uses the server to retrieve the data and i can writing a simple code to restrict that service instead of server i can access my localdata ?
All Angular services are application singletons. This means, you can change state of the service once, and the change will be 'visible' to all it's users.
The service gets instantiated when application will ask for it. While the service exists, all the controllers, etc. will receive a reference to the same service instance (I would still expect the service to be garbage collected when all the references to the service are lost).
This means, after you initialize the service, all controllers can invoke methods, etc. - all on the same instance, visible to all other instances.
Here's a jsFiddle showing the concept. When the value in factory object is changed in one controller, it is visible throughout the application. Both controllers use the modified value of testFactory.name, as it gets modified during the initialization of HelloCtrl. It is fairly easy to add two buttons that would count clicks in a field of factory's object and make both controllers display the value as it changes.
function HelloCtrl($scope, testFactory)
{
$scope.fromFactory = testFactory.sayHello();
testFactory.setName("ellitereit");
$scope.newValue = testFactory.sayHello();
}
function GoodbyeCtrl($scope, testFactory)
{
$scope.fromFactory = testFactory.sayGoodbye();
}
Related
One page is divided to several sections and each section has its own controller. The JSON objects on those sections are related. After some actions performed in one section, the state of a JSON object is changed. The change shall propagate to other sections of the page. One controller can access an object, say $scope.foo, in another controller. I have $scope.foo = ... after an action of the previous controller. The code, however, doesn't sync up the data displayed in another section.
Also, I have another set of JSON objects which is not accessible in the controller. And one object in the controller is one of those objects. How to sync them up? Is the AngularJS observer a good approach for this problem?
Based on Lux's suggestion, I create a service as the following:
angular.module('myApp.services')
.service('FooService', ['NotificationCenter', function (NotificationCenter) {
"use strict";
var foo;
function setFoo(f) {
foo = f;
}
function getFoo() {
return foo;
}
var facade = {
getFoo: getFoo,
setFoo: setFoo
};
return facade;
}]);
And in the controller of displaying the foo data, I have
// A GET web service call
FooService.setFoo(response);
$scope.currentFoo = FooService.getFoo();
And in another controller where the data is altered, I have
// after changing the data and make a PUT web service call which will retrieve the updated data
FooService.setFoo(response);
The data from the first controller isn't updated.
Do not use $rootScope, if you have a need to share data across controllers, directives, or services use a Service. In Angular, services are singletons, meaning the same instance can be shared across components, which in turn means, if you myService.setMyData('foo') in one place, you can properly access that updated data via myService.getMyData() in another component.
https://plnkr.co/edit/o1jUauTLQNLKx3dk6vrZ?p=preview
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.
What is the lifecycle of a service (or factory) in angularjs and when is it re-initialzed ?
When Angular bootstraps, it attaches the constructor functions for services to the associated module. This happens once.
angular
.module('myApp')
.service('User', function UserConstructor() {
// stuff
});
When a you try to run a controller or something that depends on a certain service, Angular will inject it for you.
app.controller('FirstCtrl', function FirstCtrlConstructor(User) {
// User is available here
});
Under the hood, angular uses this thing called $injector to do dependency injection for you. It works something like this:
var $injector = {};
$injector.cached = [];
$injector.get = function(service) {
// check if service is in this.cached
// if so, return it
// if not
// 1) instantiate the service
// 2) store it in this.cached
// 3) return it
};
So when Angular sees that it needs to inject User into FirstCtrlConstructor, it calls $injector.get('User') to get the User. Since it hasn't injected User anywhere else before, it'll hit the "if not" condition and:
Call new User().
Store it in $injector.cached for next time.
Return it.
Now let's say that we need to inject User a second time:
app.controller('SecondCtrl', function SecondCtrlConstructor(User) {
// stuff
});
Again, when Angular sees that SecondCtrlConstructor depends on User, it calls $injector.get('User') to get the User so it could inject it. This time, it hits the "if so" condition. Since we previously put User in $injector.cached, it found it there for us. Thus, User doesn't get instantiated again.
Say we have a ThirdCtrl that also depends on User. It too would find it in $injector.cached, and so it wouldn't instantiate UserConstructor. Say that we have myDirective that depends on User. The same thing would happen - it'd find it in $injector.cached and thus wouldn't instantiate it.
This is called the Singleton pattern. It's used when you don't want to instantiate something more than once. Angular uses this for services so they don't get instantiated more than once. The same is true for factories (and probably also true for providers; probably not true for values and constants).
See https://medium.com/#adamzerner/dependency-injection-in-angular-18490a9a934 for some more info.
Services/factories are only initialized once, the first time they are used. From the docs: https://docs.angularjs.org/guide/services
Angular services are:
Lazily instantiated – Angular only instantiates a service when an
application component depends on it.
Singletons – Each component dependent on a service gets a reference to the single instance
generated by the service factory.
I've been reading that Services are great for sharing data. But I am having a hard time making all data be in sync...
For example, let's say you have
an ItemService with an array of items.
a ItemListController, which shows a list of items ($scope.items = ItemService.items)
a AddItemController, a form to add items to the array (using the service).
When I add a new item, it doesn't automatically shows in the list controller. My workaround so far is to use cache:false in the router so that it refreshes every time I look at it...
I can't use $scope.$apply() because the adding happens on one controller while I want to see it on another...
You need to use $rootScope.$broadcast('item.added'); in the controller that initiates changes (AddItemController), after it successfully updates the array in the service.
Then in the ItemListController you use
$rootScope.$on('item.added', function(event){
$scope.items = ItemService.items;
});
Check this question
#Sarpdoruk There is two-way data binding between views and controllers, but not between controllers and services. By using this line $scope.items = ItemService.items; you copy 'items' from service to items in controller but you copy by value, not by reference. You can change $scope.items in your view or controller and they will get updated between the view and controller but these changes will have no effect on the service. Same thing if you update items in the service - controller will not know that something has been changed - so you need to $broadcast or $emit that something has changed from service and listen for the changes in the controller. You can also $broadcast between controllers, directives and even services (but since services have no access to $scope, you have to use $rootScope and some people frown upon that). The bottom line is, if you really have to use a $rootScope in the service because otherwise you will have to overcomplicate parts of your app, then do it and never bother with "best practice evangelist" The first rule of the app is that it has to work, only after it works you should worry about clean code. I hope it makes it clearer now.
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.