I have a abstract state whose controller get's data from a $resource async. and put the result in $rootScope.data.
.state('stateA', {
abstract: true,
url...
templateUrl...
controller: function($rootScope, Resource){
// Empty data so far
$rootScope.data = [];
Resource.query().$promise.then(function(data){
$rootScope.data = data;
})
}
})
Inheriting states are supposed to use that data. At the moment when the controllers from these states are resolved the $rootScope has not received yet the data.
When the Resource.query() is resolved in the abstract state controller, it updates the $rootScope.
Data attached to $rootScope async does not $digest? Why controllers that inherit from the abstract state do not see the changes after $rootScope.data is populated? Do I need to $broadcast and $on?
no you don't need to do any of those things, you just need to make sure your data gets properly updated and your watchers are properly set. on your child states you should do something like.
scope.$watch(function(){return $rootScope.data},function(){
})
this will let your controller know when the data array has been reassign. you can also use watch collection, it depends what type of modifications are you planning to do make to your array. assigning it it will destroy the array and create a new one, with a new address, but adding and removing elements it affects it in a different way. but bottom line you need watchers in this case because you are recreating the array.
other thing is, i really not recommend using rootScope for this purposes this is what services where made for. it might seem an overkill to use a service for this, but most of the time chances are you are also going to need to implement some data manipulation logic. and you'll end up creating a service anyways if you are doing it right
The proper approach is almost always to create a service or factory for shared data between controllers.
Related
I am beginning my development in angular and I don't know much. What I'm trying to do is that I am trying to pass a fairly large collection of data from one controller to another. This is how I managed to do it.
angular.module("myApp").controller("controllerName", function($rootScope, $scope, *...other stuff...*)
{ /* code */ }
Later there is one specific method which is accessed from outside, and I copy the collection like this:
$rootScope.selectedItems = angular.copy($scope.selected.items);
(This is an array of 5k strings)
Which is then catched in another controller. Other developers said it is unsafe to pass this through $rootScope but after the data is passed and copied into local controller, I use this to get rid of the collection in rootScope
delete $rootScope.selectedItems;
Is this a safe way to do this? It works perfectly, and nothing seems dangerous to me
As a general rule, don't use $rootScope to pass data. By using it you make your module dependent on unspecified functionality which may not be a dependency of your module. It's a structural issue which will create problems later.
Instead, use a service:
angular.module("myApp").service("myAppService", function () {
this.selectedItems = [];
});
angular.module("myApp").controller("controllerName1", function(myAppService, $scope) {
$scope.selectedItems = myAppService.selectedItems;
});
angular.module("myApp").controller("controllerName2", function(myAppService, $scope) {
$scope.selectedItems = myAppService.selectedItems;
});
It's also recommended that all your logic goes into services (and factories/providers where appropriate). Controllers should be used only to expose service functionality, unless a necessary special case can be proven. This makes the logic in services easier to unit test.
There are many service are available you should go with broadcast
Here is example for $broadcast service
https://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/
Is it advisable to store data in $rootScope.
I have a cordova app which uses sensor data which is coming every 100ms. For me to use that data in multiple controller I am using $rootScope.sensorData variable which is being refreshed every 100ms. Is it alright to use it this way? Is there a better way to do it?
Thank you
You can store it in factory. In AngularJS factory is singleton, so only instance is created.
myApp.factory('SensorSrv', function SensorSrv() {
var sensorData;
return {
setData: setData,
getData: getData
};
function setData(data) {
sensorData = data;
}
function getData() {
return sensorData;
}
});
You can also user local-storage if you want to persist the data.
I think this is not good idea to use $rootScope in entire code logic , There are lot of reasons behind that ... Instead of that you can create code login in services it is more flexible ... and also you can see this link
Best practice for using $rootscope in an Angularjs application?
From the Official Docs:
$rootScope exists, but it can be used for evil
Scopes in Angular form a hierarchy, prototypally inheriting from a root scope at the top of the tree. Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.
Occasionally there are pieces of data that you want to make global to the whole app. For these, you can inject $rootScope and set values on it like any other scope. Since the scopes inherit from the root scope, these values will be available to the expressions attached to directives like ng-show just like values on your local $scope.
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.
Conversely, don't create a service whose only purpose in life is to store and return bits of data.
--AngularJS Miscellaneous FAQ
I recommend using app.value app.value('test', 20);
Because by using $rootScope you are exposing that value to all the services which might be a security threat. By using value you can make sure where do you want to use that variable according to your requirement.
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.
Currently I have a bunch of controllers that seems a little too bulky for my liking in that they manage both the logic and the storing of data.
For instance let us take a over simplified register controller:
$scope.username = "";
$scope.password = "";
$scope.validateUsername= function(){
......
}
$scope.validatePassword= function(){
......
}
$scope.updateUserConfig = function(){
//a rest call here
}
ok so to me this seems like it is wrong because the controller is storing data, performing logic and making calls to a rest service.
I thought the controller should be the logic for the view it is used for while the "model" i.e. username and password should be somewhere else as well as the call to the rest service.
I have looked about and have seen people employing factories that are injected into the controller for the rest calls; as these factories will not store state they can be shared across the entire application, so this seems like a good idea ?
I am unsure about the data aspect though, is it normal for data to be stored within the controller or is there a better practice for this?
It is important to remember that your view must bind to the data/methods in the controller, so it must be exposed in such a way for this to be possible. In other words, there's nothing wrong with having these properties in your controller.
That being said, this data often comes from an external source or is needed in multiple views/controllers so it's common for a factory/service to return an object to a controller and then assign that object to a $scope property. This way you can abstract that layer away and share the data across your application without repeating yourself.
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.