2012-06-26 11 views
21

Niedawno wybrałem AngularJS nad ember.js dla projektu, nad którym pracuję i byłem z niego bardzo zadowolony. Jedną z zalet embera jest wbudowana obsługa "właściwości obliczeniowych" z automatycznym wiązaniem danych. Udało mi się osiągnąć coś podobnego w Angular z kodem poniżej, ale nie jestem pewien, czy jest to najlepszy sposób na zrobienie tego."Computed Properties" w AngularJS

// 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> 

chciałbym dla UI do aktualizacji, gdy currentPage zmian serwisowych navigation, który realizuje powyższy kod.

Czy to najlepszy sposób na rozwiązanie tego problemu w AngularJS? Czy istnieją (znaczące) konsekwencje związane z wydajnością korzystania z tej metody w przypadku korzystania z usługi $watch()? Czy coś takiego byłoby lepiej zrealizowane przy użyciu niestandardowych zdarzeń i $emit() lub $broadcast()?

Odpowiedz

22

Myślę, że znalazłem odpowiedź. Przykład ten może być znacznie uproszczone do:

// 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> 
+0

to lepiej rzeczywiście Jednak dlaczego trzymać wszystko razem Może powinieneś spróbować oddzielić moduł i? twoje deklaracje kontrolera? – Nimaen

+1

Cóż, moduł jest modułem dla kontrolera, zamiast utrzymywania kontrolera najwyższego poziomu – ithkuil

24

Podczas gdy self-odpowiedź działa, to w rzeczywistości nie wdrożyć właściwości obliczone. Po prostu rozwiązałeś problem, wywołując funkcję w twoim wiązaniu, aby zmusić wiązanie do zachłanności. Nie jestem w 100% pewny, że zadziała we wszystkich przypadkach, a chciwość może mieć niepożądane cechy wydajności w niektórych sytuacjach.

Pracowałem się rozwiązanie dla obliczonych własności w/zależnościami podobnymi do co EmberJS posiada:

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 }); 

Zobacz na żywo. http://jsfiddle.net/apinstein/2kR2c/3/

Warto zauważyć, że $ zakres $ watchCollection jest wydajny - - Sprawdziłem, że "assignF()" jest wywoływane tylko raz, nawet jeśli wiele zależności zmienia się jednocześnie (ten sam cykl stosowania $). "

+1

Doskonałe Zastanawiasz się, pod jakim obiektem deklarujesz te niestandardowe funkcje (w porównaniu do obiektu globalnego w przykładzie)? tam zalecana praktyka w tym kanciastym? – CodeToad

4

Należy zauważyć, że z ECMAScript 5 można teraz również zrobić coś takiego:.

// 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> 
Powiązane problemy