ngChange 在值超出隔离范围之前触发
ngChange fires before value makes it out of isolate scope
//main controller
angular.module('myApp')
.controller('mainCtrl', function ($scope){
$scope.loadResults = function (){
console.log($scope.searchFilter);
};
});
// directive
angular.module('myApp')
.directive('customSearch', function () {
return {
scope: {
searchModel: '=ngModel',
searchChange: '&ngChange',
},
require: 'ngModel',
template: '<input type="text" ng-model="searchModel" ng-change="searchChange()"/>',
restrict: 'E'
};
});
// html
<custom-search ng-model="searchFilter" ng-change="loadResults()"></custom-search>
这里有一个简化的指令来说明。当我输入输入时,我希望 loadResults
中的 console.log
准确注销我已经输入的内容。它实际上记录了一个字符,因为 loadResults
是 运行 就在主控制器中的 searchFilter
变量从指令接收新值之前。然而,在指令中登录,一切都按预期工作。为什么会这样?
我的解决方案
在我的简单示例中了解了 ngChange 发生了什么之后,我意识到我的实际问题有点复杂,因为我实际传入的 ngModel 是一个对象,我正在更改其属性,而且我正在使用此指令作为输入之一的表单验证。我发现在指令中使用 $timeout 和 $eval 解决了我所有的问题:
//main controller
angular.module('myApp')
.controller('mainCtrl', function ($scope){
$scope.loadResults = function (){
console.log($scope.searchFilter);
};
});
// directive
angular.module('myApp')
.directive('customSearch', function ($timeout) {
return {
scope: {
searchModel: '=ngModel'
},
require: 'ngModel',
template: '<input type="text" ng-model="searchModel.subProp" ng-change="valueChange()"/>',
restrict: 'E',
link: function ($scope, $element, $attrs, ngModel)
{
$scope.valueChange = function()
{
$timeout(function()
{
if ($attrs.ngChange) $scope.$parent.$eval($attrs.ngChange);
}, 0);
};
}
};
});
// html
<custom-search ng-model="searchFilter" ng-change="loadResults()"></custom-search>
您在标题中回答了您自己的问题! '='
被观看,而 '&'
未被观看
外面的某个地方angular:
输入视图值变化
下一个摘要周期:
ng-model
值更改并触发 ng-change()
ng-change 添加一个 $viewChangeListener 并调用同一个循环。
看:
ngModel.js#L714 and ngChange.js 实施。
当时$scope.searchFilter
还没有更新。 Console.log的旧值
- 下一个摘要周期:
searchFilter
通过数据绑定更新。
更新:仅作为 POC,您需要 1 个额外的周期来传播值,您可以执行以下操作。请参阅其他答案(@NewDev 以获得更清晰的方法)。
.controller('mainCtrl', function ($scope, $timeout){
$scope.loadResults = function (){
$timeout(function(){
console.log($scope.searchFilter);
});
};
});
正如另一个答案中正确指出的那样,这种行为的原因是双向绑定在 searchChange()
之前还没有机会更改外部 searchFilter
,因此,loadResults()
被调用。
但是,由于两个原因,该解决方案非常棘手。
其一,调用者(指令的用户)不需要了解 $timeout
的这些解决方法。如果不出意外,$timeout
应该在指令中而不是在视图控制器中完成。
还有两个 - OP 也犯了一个错误 - 是使用 ng-model
与此类指令的用户一起使用其他 "expectations" 。 ng-model
意味着其他指令,如验证器、解析器、格式化程序和视图更改侦听器(如 ng-change
)可以与它一起使用。要正确支持它,需要 require: "ngModel"
,而不是通过 scope: {}
绑定到它的表达式。否则,事情将无法按预期进行。
这是如何完成的 - 关于另一个示例,请参阅 the official documentation 创建自定义输入控件。
scope: true, // could also be {}, but I would avoid scope: false here
template: '<input ng-model="innerModel" ng-change="onChange()">',
require: "ngModel",
link: function(scope, element, attrs, ctrls){
var ngModel = ctrls; // ngModelController
// from model -> view
ngModel.$render = function(){
scope.innerModel = ngModel.$viewValue;
}
// from view -> model
scope.onChange = function(){
ngModel.$setViewValue(scope.innerModel);
}
}
然后,ng-change
会自动运行,其他支持 ngModel
的指令也会自动运行,例如 ng-required
.
//main controller
angular.module('myApp')
.controller('mainCtrl', function ($scope){
$scope.loadResults = function (){
console.log($scope.searchFilter);
};
});
// directive
angular.module('myApp')
.directive('customSearch', function () {
return {
scope: {
searchModel: '=ngModel',
searchChange: '&ngChange',
},
require: 'ngModel',
template: '<input type="text" ng-model="searchModel" ng-change="searchChange()"/>',
restrict: 'E'
};
});
// html
<custom-search ng-model="searchFilter" ng-change="loadResults()"></custom-search>
这里有一个简化的指令来说明。当我输入输入时,我希望 loadResults
中的 console.log
准确注销我已经输入的内容。它实际上记录了一个字符,因为 loadResults
是 运行 就在主控制器中的 searchFilter
变量从指令接收新值之前。然而,在指令中登录,一切都按预期工作。为什么会这样?
我的解决方案
在我的简单示例中了解了 ngChange 发生了什么之后,我意识到我的实际问题有点复杂,因为我实际传入的 ngModel 是一个对象,我正在更改其属性,而且我正在使用此指令作为输入之一的表单验证。我发现在指令中使用 $timeout 和 $eval 解决了我所有的问题:
//main controller
angular.module('myApp')
.controller('mainCtrl', function ($scope){
$scope.loadResults = function (){
console.log($scope.searchFilter);
};
});
// directive
angular.module('myApp')
.directive('customSearch', function ($timeout) {
return {
scope: {
searchModel: '=ngModel'
},
require: 'ngModel',
template: '<input type="text" ng-model="searchModel.subProp" ng-change="valueChange()"/>',
restrict: 'E',
link: function ($scope, $element, $attrs, ngModel)
{
$scope.valueChange = function()
{
$timeout(function()
{
if ($attrs.ngChange) $scope.$parent.$eval($attrs.ngChange);
}, 0);
};
}
};
});
// html
<custom-search ng-model="searchFilter" ng-change="loadResults()"></custom-search>
您在标题中回答了您自己的问题! '='
被观看,而 '&'
未被观看
外面的某个地方angular:
输入视图值变化
下一个摘要周期:
ng-model
值更改并触发ng-change()
ng-change 添加一个 $viewChangeListener 并调用同一个循环。 看: ngModel.js#L714 and ngChange.js 实施。
当时
$scope.searchFilter
还没有更新。 Console.log的旧值- 下一个摘要周期:
searchFilter
通过数据绑定更新。
更新:仅作为 POC,您需要 1 个额外的周期来传播值,您可以执行以下操作。请参阅其他答案(@NewDev 以获得更清晰的方法)。
.controller('mainCtrl', function ($scope, $timeout){
$scope.loadResults = function (){
$timeout(function(){
console.log($scope.searchFilter);
});
};
});
正如另一个答案中正确指出的那样,这种行为的原因是双向绑定在 searchChange()
之前还没有机会更改外部 searchFilter
,因此,loadResults()
被调用。
但是,由于两个原因,该解决方案非常棘手。
其一,调用者(指令的用户)不需要了解 $timeout
的这些解决方法。如果不出意外,$timeout
应该在指令中而不是在视图控制器中完成。
还有两个 - OP 也犯了一个错误 - 是使用 ng-model
与此类指令的用户一起使用其他 "expectations" 。 ng-model
意味着其他指令,如验证器、解析器、格式化程序和视图更改侦听器(如 ng-change
)可以与它一起使用。要正确支持它,需要 require: "ngModel"
,而不是通过 scope: {}
绑定到它的表达式。否则,事情将无法按预期进行。
这是如何完成的 - 关于另一个示例,请参阅 the official documentation 创建自定义输入控件。
scope: true, // could also be {}, but I would avoid scope: false here
template: '<input ng-model="innerModel" ng-change="onChange()">',
require: "ngModel",
link: function(scope, element, attrs, ctrls){
var ngModel = ctrls; // ngModelController
// from model -> view
ngModel.$render = function(){
scope.innerModel = ngModel.$viewValue;
}
// from view -> model
scope.onChange = function(){
ngModel.$setViewValue(scope.innerModel);
}
}
然后,ng-change
会自动运行,其他支持 ngModel
的指令也会自动运行,例如 ng-required
.