21

我最近为我正在从事的一个项目选择了 AngularJS 而不是 ember.js,到目前为止,我对它感到非常满意。ember 的一个优点是它内置了对具有自动数据绑定的“计算属性”的支持。我已经能够使用下面的代码在 Angular 中完成类似的事情,但不确定这是否是最好的方法。

// Controller
angular.module('mathSkills.controller', [])
  .controller('nav', ['navigation', '$scope', function (navigation, $scope) {
    // "Computed Property"
    $scope.$watch(navigation.getCurrentPageNumber, function(newVal, oldVal, scope) {
      scope.currentPageNumber = newVal;
    });
    $scope.totalPages = navigation.getTotalPages();
  }]);

// 'navigation' service
angular.module('mathSkills.services', [])
  .factory('navigation', function() {
    var currentPage = 0,
        pages = [];

    return {
      getCurrentPageNumber: function() {
        return currentPage + 1;
      },
      getTotalPages: function() {
        return pages.length;
      }
    };
  });

// HTML template
<div id=problemPager ng-controller=nav>
  Problem {{currentPageNumber}} of {{totalPages}}
</div>

我希望用户界面currentPagenavigation服务更改时更新,上面的代码完成了。

这是在 AngularJS 中解决这个问题的最佳方法吗?$watch()像这样使用有(显着的)性能影响吗?$emit()使用自定义事件和or会更好地完成这样的事情$broadcast()吗?

4

3 回答 3

31

虽然您的自我回答有效,但它实际上并没有实现计算属性。您只需在绑定中调用一个函数以强制绑定为贪婪即可解决问题。我不是 100% 确定它在所有情况下都有效,并且在某些情况下,贪婪可能具有不需要的性能特征。

我为具有类似于 EmberJS 的依赖项的计算属性制定了一个解决方案:

function ngCreateComputedProperty($scope, computedPropertyName, dependentProperties, f) {
  function assignF($scope) {
    var computedVal = f($scope);
    $scope[computedPropertyName] = computedVal;
  };

  $scope.$watchCollection(dependentProperties, function(newVal, oldVal, $scope) {
    assignF($scope);
  });
  assignF($scope);
};

// in some controller...
ngCreateComputedProperty($scope, 'aSquared', 'a',     function($scope) { return $scope.a * $scope.a } );
ngCreateComputedProperty($scope, 'aPlusB',   '[a,b]', function($scope) { return $scope.a + $scope.b } );

现场观看:http: //jsfiddle.net/apinstein/2kR2c/3/

值得注意的是,$scope.$watchCollection 是有效的——我验证了即使同时更改了多个依赖项(相同的 $apply 周期),“assignF()”也只被调用一次。"

于 2013-10-07T00:40:02.657 回答
24

我想我找到了答案。这个例子可以大大简化为:

// Controller
angular.module('mathSkills.controller', [])
  .controller('nav', ['navigation', '$scope', function (navigation, $scope) {
    // Property is now just a reference to the service's function.
    $scope.currentPageNumber = navigation.getCurrentPageNumber;
    $scope.totalPages = navigation.getTotalPages();
  }]);

// HTML template
// Notice the first binding is to the result of a function call.
<div id=problemPager ng-controller=nav>
  Problem {{currentPageNumber()}} of {{totalPages}}
</div>
于 2012-06-26T22:30:07.507 回答
5

请注意,使用 ECMAScript 5,您现在还可以执行以下操作:

// Controller
angular.module('mathSkills.controller', [])
  .controller('nav', function(navigation, $scope) {
    $scope.totalPages = navigation.getTotalPages();
    Object.defineProperty($scope, 'currentPageNumber', {
      get: function() {
        return navigation.getCurrentPageNumber();
      }
    });
  ]);

//HTML
<div ng-controller="nav">Problem {{currentPageNumber}} of {{totalPages}}</div>
于 2015-11-03T01:58:43.930 回答