从$ on调用时,数据绑定不会更新

时间:2014-05-31 09:19:52

标签: javascript angularjs data-binding

我有一个奇怪的错误,我无法弄明白。我最近重构了我的角度应用程序更多'angular-ish'。部分原因是将窗口调整大小事件处理程序移出指令并将其放入.run块中,它可以将Angular事件广播到整个应用程序。

那部分似乎工作正常。它监视窗口并正确传播事件。但是,当我从$ on侦听器调用我的模型操作代码时,数据绑定似乎不会触发。

.controller('channelListController', function ($scope, $rootScope, $http, $filter){
  $http.get('api/v1/channels').success(function(data){
    $scope.baseChannels = data;

    filterChannelData($scope.filter);
    //call manually since initial window resize occurs before async return
    splitChannelData($rootScope.windowAttr.columns);
  });

  //*************
  //this part doesn't trigger databinding update
  //*************
  $scope.$on('columnChange', function(){
    //window resize requires changing column number
    console.log('change detected');
    splitChannelData($rootScope.windowAttr.columns);
  });

  //*************
  //but this does
  //*************
  $scope.$watch('filter', function(newVal){
  //the filters are changed
    filterChannelData(newVal);
    //re-run split for new data
    splitChannelData($rootScope.windowAttr.columns);
  }, true);

  function filterChannelData(filter){
    if(filter){
      $scope.filteredChannels = $filter('looseCreatorComparator')($scope.baseChannels, filter.creators);
      $scope.filteredChannels = $filter('looseTagComparator')($scope.filteredChannels, filter.tags);
    } else {
      $scope.filteredChannels = $scope.baseChannels;
    }
  }

  function splitChannelData(columns){
    if(columns){
      $scope.splitChannels = [];
      for(var rep=0;rep<columns;rep++){
        $scope.splitChannels.push([]);
      }
      _.forEach($scope.filteredChannels, function(channel, index){
        $scope.splitChannels[index % columns].push(channel);
      });
    }
  }
});

因此,当我更改过滤器输入字段的内容时,$ watch会捕获它,运行我的过滤器然后触发splitChannelData。后者将我的数据集重构为一组单独的数组以供显示。视图数据绑定到$ scope.splitChannels。这很好。

但是,当广播'columnChange'事件被$ on捕获时,splitChannelData函数会触发,正确重构$ scope.splitChannels,但视图不会刷新以反映更改。

此外,如果我随后对过滤器输入字段进行另一次更改并触发$ watch处理程序,则会正确更新视图,包括$ on所做的数据集更改但未更新到早点查看。

WUT。

所以,任何人都可以告诉我为什么从$ scope调用splitDataChannel。$ watch使数据绑定正常工作但是$ scope触发的同一个调用。$ on不会?

修改: 我刚刚解决了这个bug,但仍然不明白为什么这个解决方案是必要的。我仍然希望得到反馈。

我在$ on处理程序中添加了$ apply调用:

  $scope.$on('columnChange', function(){
    //window resize requires changing column number
    console.log('change detected');
    splitChannelData($rootScope.windowAttr.columns);
    //added this
    $scope.$apply();
  });

为什么$ watch会触发数据绑定更新,而$ on则没有,而latte需要手动$ apply才能调用?

1 个答案:

答案 0 :(得分:3)

一般解释:

Angular世界的界限”并不像听起来那么复杂。

有点简化,Angular的脏检查通过将观察者存储在数组中来实现。观察者有一个watchExpression和一个listenerFunction。当触发摘要周期时,观察者被迭代。如果watchExpression的返回值自上次以来发生了更改,则会执行关联的listenerFunction。

例如,watchExpression返回字符串hello,而上次返回hell。执行listenerFunction以使用返回值hello更新特定DOM元素的文本值。

如果您更改了已观看但未触发摘要周期的变量,则更新将不会反映在用户界面中。

如果操作触发摘要周期 - 您内部边界。

如果操作未触发摘要周期 - 您之外边界。

Angular中的大多数常用功能会在内部为您自动触发摘要循环。例如指令ngClick$http服务。

具体说明:

$broadcast$emit$on不会触发摘要周期。问题不在于您的处理程序是否附加到$on,而是最有可能使用$broadcast的地方。

我的猜测是它看起来像这样:

angular.element($window).on('resize', function () {
  $rootScope.$broadcast('columnChange');
});

on函数是jqLit​​e / jQuery函数。它不会触发消化周期,这意味着它生活在Angular世界的边界之外。

您应该将其打包到$apply

angular.element($window).on('resize', function() {
  $rootScope.$apply(function () { 
    $rootScope.$broadcast('columnChange');
  });
});

不在$apply附加的处理程序中调用$on的原因是因为如果某些内容都调用$broadcast('columnChange')并触发摘要循环,则会出现$digest already in progress错误。