双向属性绑定和嵌套指令意外地表现

时间:2014-01-22 16:32:05

标签: javascript angularjs

我有嵌套的指令应该像<select>元素一样工作。当点击我的“select”元素中的一个项目时,它应该触发一个事件,该事件应该由父指令拦截并采取行动。但是,我遇到了一些奇怪的行为,其中双向数据绑定无法正确更新。

在关联的 Plunk 中,当您点击Assign "bar" as a value时,第一次点击无效。它只适用于第二次点击。

为什么?

以下是相关代码:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.name = {
    value: 'foo'
  }

  $scope.processForm = function() {
    console.log('processForm function on controller executed.')
    $scope.newValue = $scope.name.value
  }
});


app.directive('aaButtonGroup',
  function($rootScope) {
    return {
      template: '<div ng-transclude></div>',
      restrict: 'E',
      transclude: true,
      scope: {
        aaChange: '&'
      },
      link: function (scope, element, attrs) {
        $rootScope.$on('xyzClick', function() {
          console.log('aaChange expression on aaButtonGroup executed.')
          scope.aaChange()
        })
      }
    }
  }
)

app.directive('aaButtonGroupItem',
  function($rootScope) {
    return {
      template: '<a href="#" ng-click="processClick(\'bar\')">Assign "bar" as a value</a>',
      restrict: 'E',
      scope: {
        ngModel: '=',
      },
      replace: true,
      link: function(scope) {
        scope.processClick = function(key) {
          console.log('aaButtonGroupItem processClick function executed.')
          scope.ngModel = key

          $rootScope.$broadcast('xyzClick')
        }
      }
    }
  }
)

和HTML

<aa-button-group aa-change="processForm()">
  <aa-button-group-item
    ng-model="name.value"></aa-button-group-item>
</aa-button-group>
<pre>Value: {{newValue || 'Undefined'}}</pre>

这是 Plunk

1 个答案:

答案 0 :(得分:2)

首先,有两种解决方法:

$ setViewValue fix

替换:scope.ngModel = key;

使用:ngModelController.$setViewValue(key);

(并添加require: '^ngModel',ngModelController作为链接的第4个参数),如下所示:

  require: '^ngModel',
  replace: true,
  link: function(scope,element,attrs,ngModelController) {
    scope.processClick = function(key) {
      ngModelController.$setViewValue(key);
      $rootScope.$broadcast('xyzClick')
    }

$setViewValue plunker

$ watch fix

不要基于事件触发将newValue绑定到name.value,而是让Angular通过监视来处理它:

$ scope.processForm = function(){      $ scope。$ watch('name.value',function(){         $ scope.newValue = $ scope.name.value;      })   }

watch plunker

基础问题

时序。执行此行scope.ngModel = key;时,更改不会立即传播到实际的ngModel。该模型将在下一个$digest周期更新。

您可以通过添加以下日志来确认:

scope.ngModel = key;
console.log("key ",key);
console.log("view value ",ngModelController.$viewValue);
console.log("model value ",ngModelController.$modelValue);

当您单击按钮时,将显示:

  

键栏
  查看价值foo
  模型值foo

立即使用$setViewValue更新ngModel,因此您没有$digest延迟。所以:

ngModelController.$setViewValue(key);

结果:

  

键栏
  查看值栏
  模型价值栏

另一个解决方案是在$timeout放置newValue,以便在摘要周期结束后发生:{/ p>

$scope.processForm = function() {
   $timeout( function() {
     $scope.newValue = $scope.name.value;
   },0);
 }

但我认为使用$watch或直接设置模型是更清晰的解决方案。