Update scope value when changing service data

I have the following service in my application:

uaInProgressApp.factory('uaProgressService', function(uaApiInterface, $timeout, $rootScope){ var factory = {}; factory.taskResource = uaApiInterface.taskResource() factory.taskList = []; factory.cron = undefined; factory.updateTaskList = function() { factory.taskResource.query(function(data){ factory.taskList = data; $rootScope.$digest console.log(factory.taskList); }); factory.cron = $timeout(factory.updateTaskList, 5000); } factory.startCron = function () { factory.cron = $timeout(factory.updateTaskList, 5000); } factory.stopCron = function (){ $timeout.cancel(factory.cron); } return factory; }); 

Then I use it in the controller as follows:

 uaInProgressApp.controller('ua.InProgressController', function ($scope, $rootScope, $routeParams, uaContext, uaProgressService) { uaContext.getSession().then(function(){ uaContext.appName.set('Testing house'); uaContext.subAppName.set('In progress'); uaProgressService.startCron(); $scope.taskList = uaProgressService.taskList; }); } ); 

So basically my factory.taskList service update every 5 seconds, and I linked this factory.taskList to $scope.taskList . Then I tried various methods such as $apply , $digest , but changes to factory.taskList not reflected in my controller and $scope.taskList not displayed.

It stays empty in my template. Do you know how I can spread these changes?

+72
angularjs
Nov 02 '13 at 17:01
source share
6 answers

Using $watch can solve the problem; it is not the most effective solution. You might want to change the way data is stored in the service.

The problem is that you are replacing the memory cell to which the taskList is bound, each time you assign a new value to it, when the area is stuck, indicating the old location. You can see it in this panel .

Take pictures of the heap using Chrome the first time you load the plunger, and after clicking the button, you will see that the memory location indicated by the scope is never updated, and the list points to another memory location.

You can easily fix this if your service holds an object that contains a variable that may change (something like data:{task:[], x:[], z:[]} ). In this case, the "data" should never change, but any of its members can be changed whenever you need. Then you pass this data variable to the scope and, until you redefine it, trying to assign the "data" to something else, whenever the field inside the data changes, the knowledge area will know about it and will be updated correctly.

This sheet shows the same example that runs using the fix proposed above. You do not need to use observers in this situation, and if it ever happens that something is not updated in the view, then you know that all you need to do is start the $apply to update the view.

Thus, you eliminate the need for observers who often compare variables for changes and ugly settings involved in cases where you need to watch a lot of variables. The only problem with this approach is that on your view (html) you will have โ€œdataโ€. prefixing everything where you used only the variable name.

+75
Oct 02 '14 at 19:41
source share

Angular (unlike Ember and some other frameworks) does not provide special wrapped objects that remain semi-magical in sync. The objects you control are explicit javascript objects and are the same as the expression var a = b; does not bind the variables a and b , saying that $scope.taskList = uaProgressService.taskList does not bind these two values.

For this type of link -ing, angular provides $watch in $scope . You can look at the value of uaProgressService.taskList and update the value to $scope when it changes:

 $scope.$watch(function () { return uaProgressService.taskList }, function (newVal, oldVal) { if (typeof newVal !== 'undefined') { $scope.taskList = uaProgressService.taskList; } }); 

The first expression passed to the $watch function is executed in each $digest cycle, and the second argument is a function that is called using the new and old values.

+71
Nov 02 '13 at 17:17
source share

I'm not sure if this help helps, but what I am doing is associating a function with $ scope.value. for example

 angular .module("testApp", []) .service("myDataService", function(){ this.dataContainer = { valA : "car", valB : "bike" } }) .controller("testCtrl", [ "$scope", "myDataService", function($scope, myDataService){ $scope.data = function(){ return myDataService.dataContainer; }; }]); 

Then I just bind it to the DOM as

 <li ng-repeat="(key,value) in data() "></li> 

This way you can avoid using $ watch in your code.

+39
Dec 16 '14 at 9:47
source share

No $watch etc. You can simply define the following

 uaInProgressApp.controller('ua.InProgressController', function ($scope, $rootScope, $routeParams, uaContext, uaProgressService) { uaContext.getSession().then(function(){ uaContext.appName.set('Testing house'); uaContext.subAppName.set('In progress'); uaProgressService.startCron(); }); $scope.getTaskList = function() { return uaProgressService.taskList; }; }); 

Since the getTaskList function getTaskList to $scope , its return value will be evaluated (and updated) every time uaProgressService.taskList changes

+5
Jan 05 '16 at
source share

An easy alternative is that during the initialization of the controller, you subscribe to the notification template installed in the service.

Something like:

 app.controller('YourCtrl'['yourSvc', function(yourSvc){ yourSvc.awaitUpdate('YourCtrl',function(){ $scope.someValue = yourSvc.someValue; }); }]); 

And the service has something like:

 app.service('yourSvc', ['$http',function($http){ var self = this; self.notificationSubscribers={}; self.awaitUpdate=function(key,callback){ self.notificationSubscribers[key]=callback; }; self.notifySubscribers=function(){ angular.forEach(self.notificationSubscribers, function(callback,key){ callback(); }); }; $http.get('someUrl').then( function(response){ self.importantData=response.data; self.notifySubscribers(); } ); }]); 

This allows you to fine tune the settings when your controllers are updated from the service.

+3
09 Oct '15 at 16:29
source share

As Gabriel Piacenti said, a clock is not needed if you transfer changing data to an object.

BUT, for the correct update of the changed service data in the area, it is important that the value of the scope of the controller using the service data does not directly indicate a data change (field). Instead, the value of the region should point to the object that wraps the changing data.

The following code should explain this more clearly. In my example, I use NLS to translate. NLS tokens are updated using http.

Service:

 app.factory('nlsService', ['$http', function($http) { var data = { get: { ressources : "gdc.ressources", maintenance : "gdc.mm.maintenance", prewarning : "gdc.mobMaint.prewarning", } }; // ... asynchron change the data.get = ajaxResult.data... return data; }]); 

Controller output and scope

 app.controller('MenuCtrl', function($scope, nlsService) { $scope.NLS = nlsService; } ); <div ng-controller="MenuCtrl"> <span class="navPanelLiItemText">{{NLS.get.maintenance}}</span> </div> 

The above code works, but first I wanted to access my NLS tokens directly (see the following snippet), and the values โ€‹โ€‹were not updated here.

 app.controller('MenuCtrl', function($scope, nlsService) { $scope.NLS = nlsService.get; } ); <div ng-controller="MenuCtrl"> <span class="navPanelLiItemText">{{NLS.maintenance}}</span> </div> 
+2
Jan 23 '17 at 13:35
source share



All Articles