在具有隔离范围的嵌套指令中延迟双向绑定

lon*_*dok 5 angularjs angular-directive angular-components angular

我有一个控制器包含一个带有隔离范围的指令.每当用户更改指令下拉列表中的值时,我都需要通知控制器有关更改的信息.我这样做是通过为指令提供回调函数,并在指令的下拉列表中的ng-change事件上调用此函数.我不能(也不想)在父控制器中使用手表,因为我只对用户生成的更改感兴趣.

我的问题是,在调用回调时,控制器范围内的值尚未通过双向绑定更新为指令中的值(我猜这是因为回调是在同一个摘要周期中调用的)已检测到下拉列表模型的更改,但来自父控制器的模型将仅在后续周期中更新.

这是一个说明问题的plunker示例(注意控制台输出):

http://plnkr.co/edit/igW4WiV2niFyrMX2zrjM?p=preview

摘要相关部件的摘录:

控制器:

  ...
  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
    });
  };
  ...
Run Code Online (Sandbox Code Playgroud)

指示:

    ...
    template: '<select ng-model="vm.country" ng-change="vm.onChange()"  ' +
              '        ng-options="country for country in vm.countries">' +
              '</select>',
    scope: {
      country: '=',
      onChange: '&'
    },
    ...
Run Code Online (Sandbox Code Playgroud)

在父控制器回调中获取更新数据的最正确方法是什么?我可以看到$ timeout有帮助,但感觉就像代码味道,并且可能应该有一种方法来强制模型在父控制器上更新.

Kar*_*rim 5

我已经检查并修复了你的 plnkr,http: //plnkr.co/edit/8XoNEq12VmXKkyBmn9Gd?p=preview

它不起作用,因为您将原始值传递给指令,一个字符串,双向绑定仅适用于引用。

将国家/地区值更改为对象解决了问题

vm.country = 'Belarus';
Run Code Online (Sandbox Code Playgroud)

vm.country = {name: 'Belarus'};
Run Code Online (Sandbox Code Playgroud)

然后:

template: '<select ng-model="vm.country.name" ng-change="vm.onChange()"  ' +
  ' ng-options="country for country in vm.countries">' +
'</select>'
Run Code Online (Sandbox Code Playgroud)


geo*_*awg 3

我猜这是因为回调是在同一个摘要周期中调用的,其中已检测到下拉列表模型的更改,但父控制器中的模型只会在后续周期中更新。

你是一个正确的人。在指令计算其 Angular表达式后,将值从指令范围更新到父控制器的观察程序将在摘要周期上触发ng-change

在指令和父控制器之间共享公共对象引用是使其工作的一种方法。由于控制器和指令共享相同的引用,因此指令和父控制器都会立即看到任何更改。

另一种方法是将新值公开为函数的本地参数on-change

<country-chooser 
    country="vm.country" 
    country-change="vm.countryChange($event)">
</country-chooser>
Run Code Online (Sandbox Code Playgroud)

或者

<country-chooser 
    country="vm.country" 
    country-change="vm.country=$event">
</country-chooser>
Run Code Online (Sandbox Code Playgroud)

在指令中:

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',
Run Code Online (Sandbox Code Playgroud)

在父控制器中:

  vm.countryChange = function(country) {
      console.log('*** countryChange callback ***');
      console.log('country:', country);
  };
Run Code Online (Sandbox Code Playgroud)

这样,父控制器内的函数不需要等待双向绑定上的观察程序的摘要周期来将值从指令范围更新到父控制器范围。

PLNKR 上的演示。


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: '&'
    }
});
Run Code Online (Sandbox Code Playgroud)

AngularJS v1.5 引入了组件,以便更轻松地过渡到 Angular2+。

为了更轻松地过渡到 Angular2+,请避免双向 ( =) 绑定。请改用单向<绑定和表达式&绑定。

在 Angular2+ 中,双向绑定语法实际上只是属性绑定和事件绑定的语法糖。

<country-chooser [(country)]="vm.country">
</country-chooser>
Run Code Online (Sandbox Code Playgroud)

Angular2+ 将双向绑定脱糖为:

<country-chooser [country]="vm.country" (countryChange)="vm.country=$event">
</country-chooser>
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅@Angular 开发人员指南 - 模板语法(双向)