2012-06-26 16 views
21

Recientemente elegí AngularJS sobre ember.js para un proyecto en el que estoy trabajando, y he estado muy satisfecho con él hasta el momento. Una cosa buena de ember es su soporte integrado para "propiedades computadas" con enlace de datos automático. Pude lograr algo similar en Angular con el siguiente código, pero no estoy seguro de si es la mejor manera de hacerlo."Propiedades calculadas" en 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> 

Me gustaría que la interfaz de usuario para actualizar cada vez que el currentPage de los navigation cambios en el servicio, que realiza el código de seguridad.

¿Es esta la mejor manera de resolver este problema en AngularJS? ¿Hay implicaciones de rendimiento (significativas) para usar $watch() de esta manera? ¿Se lograría algo como esto usando eventos personalizados y $emit() o $broadcast()?

Respuesta

22

Creo que encontré la respuesta. Este ejemplo se puede simplificar drásticamente a:

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

Así está mejor hecho Sin embargo, ¿por qué usted se pega todo junto tal vez debería tratar de separar el módulo y? sus declaraciones de controlador? – Nimaen

+1

Bueno, el módulo es el módulo para el controlador, en lugar de mantener un controlador de nivel superior – ithkuil

24

Mientras que su auto-respuesta funciona, en realidad no implementar propiedades calculadas. Simplemente resolvió el problema llamando a una función en su enlace para obligar a que el enlace sea codicioso. No estoy 100% seguro de que funcione en todos los casos, y la codicia podría tener características de rendimiento no deseadas en algunas situaciones.

trabajé una solución para el inmueble calculadas w/dependencias similares a lo EmberJS tiene:

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

verlo en directo:. http://jsfiddle.net/apinstein/2kR2c/3/

Vale la pena señalar que $ alcance $ watchCollection es eficiente - - Comprobé que "assignF()" solo se llama una vez, incluso si se cambian varias dependencias simultáneamente (el mismo ciclo $ apply). "

+1

Excelente. Solo me pregunto bajo qué objeto declaras estas funciones personalizadas (en comparación con el objeto global en el ejemplo)? Hay una práctica recomendada para esto en angular? – CodeToad

4

Tenga en cuenta que con ECMAScript 5 ahora también puede hacer algo como esto:.

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