我遇到了一些问题,解决了我正在研究的一些Angularjs功能的问题。
基本思想是我有一个系统,在允许用户前进到应用程序的下一部分之前必须满足某些条件。这方面的一个例子是,用户必须同时添加评论,然后单击链接(在真实应用程序中,这是文件下载),以便他们前进。
您可以在此处查看完整示例:https://jsfiddle.net/d81xxweu/10/
我将假设HTML非常自我解释,并继续我正在使用我的Angular模块。我的app声明和初始化如下:
var myApp = angular.module('myApp', ['ngRoute']);
myApp.run(function ($rootScope) {
// Both of these must be met in order for the user to proceed with 'special-button'
$rootScope.criteria = {
criteria1: false,
criteria2: false
};
});
这很简单。我将一个名为criteria的对象附加到应用程序的根范围,以使我的指令和控制器可以访问它。我有一个指令,用于呈现允许用户在符合条件后前进的链接。在此示例中,链接的文本从“等待...”更改为“单击以继续”以指示我们可以前进。
myApp.directive('specialButton', function ($rootScope) {
return {
scope: true,
template: "<a href='#'>{{ linkText }}</a>",
replace: true,
link: function (scope, el, attrs) {
scope.linkText = 'Waiting...';
var setLinkState = function(currentCriteria) {
var criteriaMet = true;
for(var k in $rootScope.criteria) {
if($rootScope.criteria[k] == false) {
criteriaMet = false;
}
}
if(criteriaMet) {
scope.linkText = 'Click to proceed';
}
};
// Watch for changes to this object at the root scope level
$rootScope.$watchCollection('criteria', function(newValues) {
setLinkState(newValues);
});
}
};
});
因此,为了触发我们在此指令上设置的watch语句,我可以添加此控制器允许的注释:
myApp.controller('comments', function ($scope, $rootScope) {
$scope.commentText = '';
$scope.comments = [];
$scope.addComment = function () {
$scope.comments.push({ commentText: $scope.commentText });
$scope.commentText = ''
// When the user adds a comment they have met the first criteria
$rootScope.criteria.criteria1 = true;
};
});
之前是我的控制器,用于显示/添加评论。我在这里将criteria1设置为true表示用户已添加评论。这实际上工作正常,并且specialButton指令中的$ watchCollection按预期调用。
当我尝试从必须单击以便前进的链接执行相同的操作时,会出现问题。这是使用指令呈现的,因为我的理解是,在这种情况下,指令比控制器更有意义,与注释列表/表单不同。
myApp.directive('requiredLink', function($rootScope) {
return {
scope: true,
template: "<a href='#'>Click me!</a>",
replace: true,
link: function(scope, el, attrs) {
el.bind('click', function(evt) {
evt.preventDefault();
// When the user clicks this link they have met the second criteria
$rootScope.criteria.criteria2 = true;
});
}
};
});
正如你在这里看到的那样,我传入$ rootScope,就像在控制器中一样。但是,当我将criteria2设置为true时,不会触发$ watchCollection。
所以最终发生的事情是,如果我先添加注释,然后单击另一个按钮,我看不到specialButton更新其文本,因为第二个更改从不触发手表。但是,如果我先点击链接,然后按预期添加评论,specialButton更新。点击requiredLink IS更新数据,但不会触发手表。因此,当我添加注释并触发$ watch时,它会看到BOTH已设置为true。
提前感谢您在解决此问题时提供的任何帮助;我很感激你的时间。
答案 0 :(得分:1)
你的实际问题是你是从角度上下文之外的事件更新$rootScope
,所以它的意思是角度绑定不会更新,因为在这种情况下,摘要周期不会被触发。您需要使用$apply()
$rootScope
方法手动触发它
el.bind('click', function(evt) {
evt.preventDefault();
// When the user clicks this link they have met the second criteria
$rootScope.criteria.criteria2 = true;
$rootScope.$apply(); //this will run digest cycle & will fire `watchCollection` `$watcher`
});
虽然此解决方案有效,但我建议您改用服务 使用
$rootScope
对于使用服务的实施,您需要遵循以下有助于您的事情。
您的服务应该使用对象形式的criteria
变量,应该遵循dot rule
,以便使用JavaScript原型更新相应的引用
<强> SEVICE 强>
app.service('dataService', function(){
this.criteria = {
criteria1: false,
criteria2: false
};
//...here would be other sharable data.
})
无论何时你想在任何需要的地方使用它,都可以在控制器,指令,过滤器的任何地方注入它。
在对指令中的服务变量进行监视时,您需要执行以下操作。
<强>指令强>
myApp.directive('specialButton', function (dataService) {
return {
scope: true,
template: "<a href='#'>{{ linkText }}</a>",
replace: true,
link: function (scope, el, attrs) {
//.. other code
// deep watch will watch on whole object making last param true
scope.$watch(function(){
return dataService.criteria //this will get get evaluated on criteria change
}, function(newValues) {
setLinkState(newValues);
}, true);
}
};
});