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
Related
I'm currently using one controller for my web app. Data is loaded from a JSON file and shown to the user via ng-repeat. When a user makes a selection, only data for the user's selection is in the scope.
I'd like to be able to keep the same scope data and use it across different web pages (or states using UI-Router).
I've looked into using ui-router but it seems like the controller would be refreshed with every state change.
I'd prefer to use ui-router due to design requirements.
Part of my controller code:
(function() {
"use strict";
angular
.module("parkit", ["ngMap"])
.controller("parkitController", function($scope, $rootScope, $http,
parkitFactory, NgMap) {
parkitFactory.getSpots().then(function(spots) {
$scope.spots = spots.data;
});
$scope.showSpot = function(spot) {
$scope.spot = spot;
}
});
})();
Factory code for loading JSON data:
(function() {
"use strict";
angular.module("parkit")
.factory("parkitFactory", function($http) {
function getSpots() {
return $http.get('/data/spots.json');
}
return {
getSpots: getSpots
}
});
})();
As it has been answered before, you can use a factory or service to keep track of the selected item(s). This would store the selected values in the instance of the service/factory and therefore would be lost if someone refreshes the page.
A more resilient, and in my opinion beautiful solution, would be to add the selected item(s) as state parameter in ui-router. Using this method, you will also be able to deep-link to certain selected states and if someone refreshes the page, the same items would still be selected, as you would add your state parameters in the url.
See URL Parameters in the documentation: https://github.com/angular-ui/ui-router/wiki/URL-Routing
You may probably create a new property in the factory function to keep track of the selected item.Set the property when user does a selection. Get the property in other components whereever you need to use the data.
use $rootScope instead of $scope to save your data, That will allow you to use it anywhere in controllers of same domain.
example: $rootScope.yourData = yourData; and then you can assign $rootScope.yourData to any controller in same domain.
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 want to share data between two controllers using some factory method. The controller Ctrl1 belongs to the first DIV tag from which HTTP request is sent and data from server is received in streaming fashion. And in other DIV tag, I want to display that result as a list (ng-repeat) which keeps on updating as the HTTP result in first DIV gets updated (also, second DIV tag starts displaying results as soon as first DIV tag gets first response, and first DIV disappears). I know how to pass data between controllers one time (e.g. ng-click="pass()"), but not sure how to achieve this. I am looking for an answer which does not uses $rootScope.
You should create a service to contain the data, then inject the service in to each controller.
For example, lets define a 'myService' service with one property 'foo' equalling a string 'bar':
angular.module('myModule').service('myService', function() {
this.foo = 'bar';
});
We can now use this in a controller as so:
angular.module('myModule').controller('MyCtrl', function($scope, myService) {
$scope.myService = myService;
});
This is now accessible in the template as {{myService.foo}} or ng-model="myService.foo" as usual.
You can then use it in a second controller in a similar way, when you update the property 'foo' in either controller the changes will persist between controllers. This is because services in angular are singletons. What you're doing by defining a service like this is creating a singleton 'myService' which can be injected wherever you want and will always be instantiated only once.
If you need to watch for changes within the controller code, you can use $scope.$watch() as usual on myService.foo.
I have a page with a form in a modal. When the form in the modal is ready, I need to close the modal and display a message on the page. I would like the page and the modal to be separate controllers because they have different responsibilities. There are two methods I have found to notify the page that the form is ready:
Create a service which both controllers get injected and call methods on that
Make the modal controller a child of the page controller and let them share an object, like here: http://fdietz.github.io/recipes-with-angular-js/controllers/sharing-models-between-nested-controllers.html
The Angular documentation on scopes seem to say that controllers should not share variables but use services, but is that really the best way to go in this case?
Use services to share information about controllers, but instead of injecting the controllers to your service, inject the service to the controllers. Also, if you want to remain the binding between your view and your data, you need to use objects instead of primitive variables.
angular.module("MyApp", [])
.factory("Data", function() {
return { msg: "Shareable/Bindable data" }
})
.controller("One", function($scope, Data){
$scope.foo = Data;
})
.controller("Two", function($scope, Data){
$scope.bar = Data;
})
In this example, I could had just returned the data directly, instead of wrapping it in an object on my service. However, if we had done that (return "..." and $scope.foo = Data) , the variables {{foo}} or {{bar}} would only have a "shadow" copy of the factory information. Thus, we need to use {{foo.msg}} in our view and the message wrapping.
The full example is here in Codepen.io. Remove the { msg } and return the string instead to see what I mean.
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();
}