在具有隔离范围的嵌套指令中延迟双向绑定
Delay in Two-way binding in a nested directive with isolate scope
我有一个控制器,其中包含一个独立作用域的指令。每当用户更改指令下拉列表中的值时,我都需要将更改通知控制器。我通过为指令提供回调函数,并在指令下拉列表中的 ng-change 事件上调用此函数来做到这一点。我不能(也不想)在父控制器中使用手表,因为我只对此处用户生成的更改感兴趣。
我的问题是,在调用回调时,控制器范围内的值尚未通过双向绑定更新为指令中的值(我猜这是因为回调是在已检测到下拉列表模型更改的相同摘要周期,但父控制器中的模型只会在后续周期中更新)。
这是一个说明问题的 plunker 示例(注意控制台输出):
http://plnkr.co/edit/igW4WiV2niFyrMX2zrjM?p=preview
plunker相关部分摘录:
控制器:
...
vm.countryChanged = function() {
console.log('*** countryChanged callback ***');
console.log('country:', vm.country); // old value here
$timeout(function() {
console.log('country (inside timeout):', vm.country); // new value here
});
};
...
指令:
...
template: '<select ng-model="vm.country" ng-change="vm.onChange()" ' +
' ng-options="country for country in vm.countries">' +
'</select>',
scope: {
country: '=',
onChange: '&'
},
...
在父控制器回调中获取更新数据的最正确方法是什么?我可以看到 $timeout 有帮助,但感觉就像代码的味道,并且可能应该有一种方法可以强制模型在父控制器上更新。
我已经检查并修复了你的 plnkr,http://plnkr.co/edit/8XoNEq12VmXKkyBmn9Gd?p=preview
它不起作用,因为您将原始值传递给指令,一个字符串,双向绑定仅适用于引用。
将国家/地区值更改为对象解决了问题
vm.country = 'Belarus';
到
vm.country = {name: 'Belarus'};
然后:
template: '<select ng-model="vm.country.name" ng-change="vm.onChange()" ' +
' ng-options="country for country in vm.countries">' +
'</select>'
I guess this is because the callback is called in the same digest-cycle where the changes on the dropdown's model has been detected, but the models from the parent controller will only be updated in the subsequent cycles.
你是对的。将值从指令范围更新到父控制器的观察者在 ng-change
指令评估其 Angular 表达式后的摘要循环中触发。
在指令和父控制器之间共享公共对象引用是使其工作的一种方法。由于控制器和指令共享相同的引用,因此指令和父控制器都会立即看到任何更改。
另一种方法是将新值公开为 on-change
函数的局部参数:
<country-chooser
country="vm.country"
country-change="vm.countryChange($event)">
</country-chooser>
或
<country-chooser
country="vm.country"
country-change="vm.country=$event">
</country-chooser>
在指令中:
app.directive('countryChooser', function() {
return {
template: `<select ng-model="$ctrl.country"
ng-change="$ctrl.countryChange({'$event': vm.country})"
ng-options="country for country in $ctrl.countries">
</select>
`,
scope: {
country: '<',
countryChange: '&'
},
bindToController: true,
controllerAs: '$ctrl',
在父控制器中:
vm.countryChange = function(country) {
console.log('*** countryChange callback ***');
console.log('country:', country);
};
这样,父控制器中的函数不需要等待双向绑定上的观察者的摘要周期,以将值从指令范围更新到父控制器范围。
AngularJS v1.5 组件和制作它们 Angular2+ 就绪
app.component('countryChooser', {
template: `<select ng-model="$ctrl.country"
ng-change="$ctrl.countryChange({'$event': vm.country})"
ng-options="country for country in $ctrl.countries">
</select>
`,
bindings: {
countries: '<'
country: '<',
countryChange: '&'
}
});
AngularJS v1.5 引入了 components 使过渡到 Angular2+ 更容易。
要更轻松地过渡到 Angular2+,请避免双向 (=
) 绑定。而是使用单向 <
绑定和表达式 &
绑定。
在 Angular2+ 中,双向绑定语法实际上只是 属性 绑定和事件绑定的语法糖。
<country-chooser [(country)]="vm.country">
</country-chooser>
Angular2+ 脱糖双向绑定:
<country-chooser [country]="vm.country" (countryChange)="vm.country=$event">
</country-chooser>
有关详细信息,请参阅 @Angular Developer Guide - Template Syntax (two-way)
我有一个控制器,其中包含一个独立作用域的指令。每当用户更改指令下拉列表中的值时,我都需要将更改通知控制器。我通过为指令提供回调函数,并在指令下拉列表中的 ng-change 事件上调用此函数来做到这一点。我不能(也不想)在父控制器中使用手表,因为我只对此处用户生成的更改感兴趣。
我的问题是,在调用回调时,控制器范围内的值尚未通过双向绑定更新为指令中的值(我猜这是因为回调是在已检测到下拉列表模型更改的相同摘要周期,但父控制器中的模型只会在后续周期中更新)。
这是一个说明问题的 plunker 示例(注意控制台输出):
http://plnkr.co/edit/igW4WiV2niFyrMX2zrjM?p=preview
plunker相关部分摘录:
控制器:
...
vm.countryChanged = function() {
console.log('*** countryChanged callback ***');
console.log('country:', vm.country); // old value here
$timeout(function() {
console.log('country (inside timeout):', vm.country); // new value here
});
};
...
指令:
...
template: '<select ng-model="vm.country" ng-change="vm.onChange()" ' +
' ng-options="country for country in vm.countries">' +
'</select>',
scope: {
country: '=',
onChange: '&'
},
...
在父控制器回调中获取更新数据的最正确方法是什么?我可以看到 $timeout 有帮助,但感觉就像代码的味道,并且可能应该有一种方法可以强制模型在父控制器上更新。
我已经检查并修复了你的 plnkr,http://plnkr.co/edit/8XoNEq12VmXKkyBmn9Gd?p=preview
它不起作用,因为您将原始值传递给指令,一个字符串,双向绑定仅适用于引用。
将国家/地区值更改为对象解决了问题
vm.country = 'Belarus';
到
vm.country = {name: 'Belarus'};
然后:
template: '<select ng-model="vm.country.name" ng-change="vm.onChange()" ' +
' ng-options="country for country in vm.countries">' +
'</select>'
I guess this is because the callback is called in the same digest-cycle where the changes on the dropdown's model has been detected, but the models from the parent controller will only be updated in the subsequent cycles.
你是对的。将值从指令范围更新到父控制器的观察者在 ng-change
指令评估其 Angular 表达式后的摘要循环中触发。
在指令和父控制器之间共享公共对象引用是使其工作的一种方法。由于控制器和指令共享相同的引用,因此指令和父控制器都会立即看到任何更改。
另一种方法是将新值公开为 on-change
函数的局部参数:
<country-chooser
country="vm.country"
country-change="vm.countryChange($event)">
</country-chooser>
或
<country-chooser
country="vm.country"
country-change="vm.country=$event">
</country-chooser>
在指令中:
app.directive('countryChooser', function() {
return {
template: `<select ng-model="$ctrl.country"
ng-change="$ctrl.countryChange({'$event': vm.country})"
ng-options="country for country in $ctrl.countries">
</select>
`,
scope: {
country: '<',
countryChange: '&'
},
bindToController: true,
controllerAs: '$ctrl',
在父控制器中:
vm.countryChange = function(country) {
console.log('*** countryChange callback ***');
console.log('country:', country);
};
这样,父控制器中的函数不需要等待双向绑定上的观察者的摘要周期,以将值从指令范围更新到父控制器范围。
AngularJS v1.5 组件和制作它们 Angular2+ 就绪
app.component('countryChooser', {
template: `<select ng-model="$ctrl.country"
ng-change="$ctrl.countryChange({'$event': vm.country})"
ng-options="country for country in $ctrl.countries">
</select>
`,
bindings: {
countries: '<'
country: '<',
countryChange: '&'
}
});
AngularJS v1.5 引入了 components 使过渡到 Angular2+ 更容易。
要更轻松地过渡到 Angular2+,请避免双向 (=
) 绑定。而是使用单向 <
绑定和表达式 &
绑定。
在 Angular2+ 中,双向绑定语法实际上只是 属性 绑定和事件绑定的语法糖。
<country-chooser [(country)]="vm.country">
</country-chooser>
Angular2+ 脱糖双向绑定:
<country-chooser [country]="vm.country" (countryChange)="vm.country=$event">
</country-chooser>
有关详细信息,请参阅 @Angular Developer Guide - Template Syntax (two-way)