Подтвердить что ты не робот

"Вычислимые свойства" в AngularJS

Недавно я выбрал 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>

Я бы хотел, чтобы пользовательский интерфейс обновлялся всякий раз, когда изменяется currentPage службы navigation, которую выполняет вышеприведенный код.

Это лучший способ решить эту проблему в AngularJS? Имеются ли (значительные) последствия для использования $watch() как это? Было бы лучше сделать что-то подобное с помощью пользовательских событий и $emit() или $broadcast()?

4b9b3361

Ответ 1

Я думаю, что нашел ответ. Этот пример можно значительно упростить:

// Controller
angular.module('mathSkills.controller', [])
  .controller('nav', ['navigation', '$scope', function (navigation, $scope) {
    // Property is now just a reference to the service 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>

Ответ 2

Пока ваш самопомощь работает, он фактически не реализует вычисленные свойства. Вы просто решили проблему, вызвав функцию в вашей привязке, чтобы заставить связывание быть жадным. Я не уверен на 100%, что он будет работать во всех случаях, и жадность может иметь нежелательные характеристики производительности в некоторых ситуациях.

Я разработал решение для вычисленных свойств w/зависимостей, похожих на то, что имеет 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 эффективен - я проверил, что "assignF()" вызывается только один раз, даже если несколько зависимостей изменяются одновременно (одинаковый цикл $apply). "

Ответ 3

Обратите внимание, что с помощью 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>