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.
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.
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 may be a very basic question but I hope the gurus here at Stackoverflow will be able to provide a comprehensive and educating answer.
When I press the back button in my angular app, are the controllers fetch data from the backend again? And is that possible to avoid that, and just load what was in the page previously, including various states it had such as ordering of rows in a table?
Thanks
When you change routes, your attached controller functions will rerun. Inside your controllers, or services, whatever is fetching the data, you can save contents to a parent scope, such as $rootScope or you can save to the browser session storage, and check to see if either of those things have been populated before fetching data.
function controller ($scope, $rootScope, $http) {
if (! $rootScope.savedData) {
$http.get('data').success(function (data) {
$rootScope.savedData = data;
$scope.data = data;
});
}
else $scope.data = $rootScope.savedData;
}
I was told if you need to share between controllers you should use a service. I have controller A, which is a list of news websites, and controller B which is a list of articles on the sites from controller A. Controller B contains the list of articles and an iframe to display the articles. But when you click on controller A it should fade out the iframe and fade in the list. In order to accomplish this I give Controller B's scope to a service that is injected into both controller A and controller B. My question is whether or not it's okay to do that.
Basically, I do this:
app.factory("sharedService", function () {
var $scope = null;
return {
SetScope: function (scope) {
$scope = scope;
},
ControllerB_Action: function () {
$scope.doSomething();
}
};
});
app.controller("controllerA", ["$scope", "sharedService", function ($scope, sharedService) {
$scope.onaction = function () {
sharedService.ControllerB_Action();
}
}]);
app.controller("controllerB", ["$scope", "sharedService", function ($scope, sharedService) {
sharedService.SetScope($scope);
}]);
I would say its not a good pattern, since basically, the $scope is an Object that represents your current view or DOM-State. A controllers (and/or Link-Functions of directives) are the Glue between this state and your Application-Logic - so in my opinion, the $scope-Object should always remain inside the Controllers/Links.
Therefore if you wanna share Data between 2 Controllers, you should extract what you wanna share inside the Service, but not put the whole scope there (since it has lots of additional information that you dont need and want inside both controllers).
What you can do is simply link the data you wanna share by reference - that way, your Service will also Sync the Data between the two Scopes.
There's probably a world of ways of doing this, but I'll tell you what I would do:
I'd make use of event emmiters.
Disclaimer: I haven't tested this code
Assuming that Controller B is nested in Controller A:
Controller A
var controllerBFunction_A;
$scope.$on('EventFromControllerB',function(data){
controllerBFunction_A = data.sharedFunctions.controllerBFunction_A;
});
Controller B
$scope.$emit('EvenFromControllerB',{
sharedFunctions: [
controllerBFunction_A,
controllerBFunction_B,
someOtherObject
]
});
I think this could work. In my opinion this has the benefit of selecting what you want to share between those controllers...but probably there's a more elegant way of doing this.
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.