Angularjs 更改服务数据时更新范围值
我的应用程序中有以下服务:Angularjs 更改服务数据时更新范围值,angularjs,Angularjs,我的应用程序中有以下服务: uaInProgressApp.factory('uaProgressService', function(uaApiInterface, $timeout, $rootScope){ var factory = {}; factory.taskResource = uaApiInterface.taskResource() factory.taskList = []; factory.cron
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;
});
然后我在控制器中使用它,如下所示:
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;
});
}
);
因此,基本上我的服务每5秒更新一次工厂.taskList
,我将此工厂.taskList
链接到$scope.taskList
。然后我尝试了不同的方法,如$apply
,$digest
,但是工厂上的更改。任务列表
不会反映在我的控制器和视图中$scope.taskList
它在我的模板中保持为空。您知道如何传播这些更改吗?Angular(与Ember和其他一些框架不同)不提供特殊的包装对象,它们半神奇地保持同步。您正在操作的对象是普通的javascript对象,就像说var a=b
不链接变量a
和b
,表示$scope.taskList=uaProgressService。taskList
不链接这两个值
对于这种链接
-ing,角度
提供。您可以查看uaProgressService.taskList
的值,并在其更改时更新$scope
上的值:
$scope.$watch(函数(){return uaProgressService.taskList},函数(newVal,oldVal){
if(typeof newVal!=“未定义”){
$scope.taskList=uaProgressService.taskList;
}
});
传递给
$watch
函数的第一个表达式在每个参数上执行,第二个参数是使用新值和旧值调用的函数。使用$watch
可以解决问题,但这不是最有效的解决方案。您可能希望更改在服务中存储数据的方式
问题在于,每次为任务列表指定新值时,都会替换与其关联的内存位置,而作用域一直指向旧位置。你可以看到这种情况正在发生
当您第一次加载plunk时,使用Chrome创建一个堆快照,单击按钮后,您将看到作用域指向的内存位置从未更新,而列表指向不同的内存位置
您可以通过让您的服务持有一个包含可能更改的变量(类似于数据:{task:[],x:[],z:[]}
)的对象来轻松解决此问题。在这种情况下,不应更改“数据”,但其任何成员都可以在需要时更改。然后将此数据变量传递给作用域,只要不通过尝试将“数据”分配给其他对象来覆盖它,只要数据中的字段发生更改,作用域就会知道并正确更新
显示了使用上面建议的修复程序运行的相同示例。在这种情况下不需要使用任何监视程序,如果发生视图上没有更新某些内容的情况,您知道您需要做的就是运行scope$apply
来更新视图
这样,您就不需要经常比较变量变化的观察者,也不需要在需要观察许多变量的情况下进行丑陋的设置。这种方法的唯一问题是,在视图(html)上,您将有“数据”。在以前只有变量名的地方加上前缀。我不确定这是否有帮助,但我正在做的是将函数绑定到$scope.value。比如说
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;
};
}]);
然后我将它绑定到DOM中作为
<li ng-repeat="(key,value) in data() "></li>
通过这种方式,您可以避免在代码中使用$watch。轻量级替代方案是,在控制器初始化期间,您订阅在服务中设置的通知程序模式
比如:
app.controller('YourCtrl'['yourSvc', function(yourSvc){
yourSvc.awaitUpdate('YourCtrl',function(){
$scope.someValue = yourSvc.someValue;
});
}]);
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();
}
);
}]);
该服务有如下功能:
app.controller('YourCtrl'['yourSvc', function(yourSvc){
yourSvc.awaitUpdate('YourCtrl',function(){
$scope.someValue = yourSvc.someValue;
});
}]);
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();
}
);
}]);
这可以让您在控制器从服务刷新时更仔细地进行微调。不需要$watch
等。您可以简单地定义以下内容
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;
};
});
由于函数getTaskList
属于$scope
它的返回值将在uaProgressService的每次更改时进行评估(和更新)。taskList
如Gabriel Piacenti所说,如果将更改的数据包装到对象中,则不需要手表
但是,为了正确更新作用域中已更改的服务数据,使用服务数据的控制器的作用域值不能直接指向正在更改的数据(字段),这一点很重要。相反,范围值必须指向包装更改数据的对象
下面的代码应该更清楚地解释这一点。在我的示例中,我使用NLS服务进行翻译。NLS令牌正在通过http进行更新
服务:
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;
}]);
控制器和作用域表达式
app.controller('MenuCtrl', function($scope, nlsService)
{
$scope.NLS = nlsService;
}
);
<div ng-controller="MenuCtrl">
<span class="navPanelLiItemText">{{NLS.get.maintenance}}</span>
</div>
app.controller('MenuCtrl',函数($scope,nls服务)
{
$scope.NLS=nlsService;
}
);
{{NLS.get.maintenance}
上面的代码可以工作,但首先我想直接访问我的NLS令牌(请参见下面的代码片段),而这里的值没有更新
app.controller('MenuCtrl', function($scope, nlsService)
{
$scope.NLS = nlsService.get;
}
);
<div ng-controller="MenuCtrl">
<span class="navPanelLiItemText">{{NLS.maintenance}}</span>
</div>
app.controller('MenuCtrl',函数($scope,nls服务)
{
$scope.NLS=nlsService.get;
}
);
{{NLS.maintenance}
好的,我明白。我尝试了你的代码片段,现在我的应用程序按预期运行:)很多手表会影响应用程序的性能吗?尽管有时,它对大多数应用程序都相当有效。我失明了这么久,谢谢。我认为,因为它是一个独生子,所以属性会以一种神奇的方式传播:SI花了我好几天的时间才找到这个答案。谢谢我很惊讶这个用例没有更好的文档化,因为这是我使用服务(rpc/pubsub)的唯一原因。$watch在这种情况下是一种攻击。其他两个答案描述了正确的技巧,特别是@Gabriel Piacenti返回服务时“新”生命+iLife的原因是什么?在这种情况下,它将允许您返回一个类,删除新的将破坏代码。我倾向于选择这个结构而不是对象结构