Angularjs 当从指令更新值时未触发 $rootScope 上的 $watchCollection
Angularjs $watchCollection on $rootScope not triggered when values updated from directive
我 运行 在解决我正在处理的一些 Angularjs 功能的问题时遇到了一些问题。
我的基本想法是我有一个系统,在允许用户前进到应用程序的下一部分之前,必须满足某些条件。这方面的一个例子是,用户必须同时添加评论,然后单击 link(在实际应用中,这是一个文件下载)才能让他们前进。
您可以在此处查看完整示例: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 的对象附加到应用程序的根范围,以便我的指令和控制器可以访问它。我有一个呈现 link 的指令,它允许用户在满足条件后前进。在此示例中,link 的文本从 "Waiting..." 更改为 "Click to proceed",表示我们可以前进。
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;
};
});
前一个是我的 displaying/adding 评论控制器。我在这里将 criteria1 设置为 true 以指示用户已添加评论。这实际上工作正常,并且按预期调用了 specialButton 指令中的 $watchCollection。
当我尝试从必须单击才能前进的 link 执行相同的操作时出现问题。这是用指令呈现的,因为据我了解,在这种情况下,指令比控制器更有意义,这与注释 list/form.
不同
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 更新其文本,因为第二次更改永远不会触发手表。但是,如果我先单击 link,然后添加评论,specialButton 会按预期更新。单击 requiredLink 正在更新数据,但不会触发手表。因此,当我添加评论并触发 $watch 时,它会看到两者都已设置为 true。
在此先感谢您为解决此问题提供的任何帮助;感谢您的宝贵时间。
您的实际问题是您正在从 angular 上下文之外的事件更新 $rootScope
,因此很明显 angular 绑定不会更新,因为摘要循环不会更新在那种情况下被解雇。您需要使用 $rootScope
的 $apply()
方法手动触发它
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`
});
Though this solution work but I'll suggest you to use service instead
of using $rootScope
要使用服务实施,您需要遵循以下对您有帮助的事情。
您的服务应该使用对象形式的 criteria
变量,应该遵循 dot rule
以便相应的引用将使用 JavaScript 原型
进行更新
服务
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);
}
};
});
我 运行 在解决我正在处理的一些 Angularjs 功能的问题时遇到了一些问题。
我的基本想法是我有一个系统,在允许用户前进到应用程序的下一部分之前,必须满足某些条件。这方面的一个例子是,用户必须同时添加评论,然后单击 link(在实际应用中,这是一个文件下载)才能让他们前进。
您可以在此处查看完整示例: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 的对象附加到应用程序的根范围,以便我的指令和控制器可以访问它。我有一个呈现 link 的指令,它允许用户在满足条件后前进。在此示例中,link 的文本从 "Waiting..." 更改为 "Click to proceed",表示我们可以前进。
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;
};
});
前一个是我的 displaying/adding 评论控制器。我在这里将 criteria1 设置为 true 以指示用户已添加评论。这实际上工作正常,并且按预期调用了 specialButton 指令中的 $watchCollection。
当我尝试从必须单击才能前进的 link 执行相同的操作时出现问题。这是用指令呈现的,因为据我了解,在这种情况下,指令比控制器更有意义,这与注释 list/form.
不同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 更新其文本,因为第二次更改永远不会触发手表。但是,如果我先单击 link,然后添加评论,specialButton 会按预期更新。单击 requiredLink 正在更新数据,但不会触发手表。因此,当我添加评论并触发 $watch 时,它会看到两者都已设置为 true。
在此先感谢您为解决此问题提供的任何帮助;感谢您的宝贵时间。
您的实际问题是您正在从 angular 上下文之外的事件更新 $rootScope
,因此很明显 angular 绑定不会更新,因为摘要循环不会更新在那种情况下被解雇。您需要使用 $rootScope
$apply()
方法手动触发它
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`
});
Though this solution work but I'll suggest you to use service instead of using
$rootScope
要使用服务实施,您需要遵循以下对您有帮助的事情。
您的服务应该使用对象形式的 criteria
变量,应该遵循 dot rule
以便相应的引用将使用 JavaScript 原型
服务
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);
}
};
});