javascript - ¿Cómo hacer filtrado bidireccional en AngularJS?
Una de las cosas interesantes que AngularJS puede hacer es aplicar un filtro a una expresión de enlace de datos en particular, lo cual es una forma conveniente de aplicar, por ejemplo, formato de moneda o fecha específico de la cultura a las propiedades de un modelo. También es agradable tener propiedades calculadas en el scope. El problema es que ninguna de estas características funciona con escenarios de enlace de datos bidireccional, solo con enlace de datos unidireccional del scope a la vista. Esto parece ser una omisión evidente en una biblioteca que por lo demás es excelente, ¿o me estoy perdiendo de algo?
En KnockoutJS, podía crear una propiedad computada de lectura/escritura, lo que me permitía especificar un par de funciones, una que se llama para obtener el valor de la propiedad y otra que se llama cuando la propiedad se establece. Esto me permitía implementar, por ejemplo, una entrada consciente de la cultura, dejando que el usuario escriba "$1.24" y analizándolo en un número flotante en el ViewModel, y que los cambios en el ViewModel se reflejaran en la entrada.
Lo más parecido que pude encontrar a esto es el uso de $scope.$watch(propertyName, functionOrNGExpression); Esto me permite tener una función invocada cuando una propiedad en el $scope cambia. Pero esto no resuelve, por ejemplo, el problema de entrada sensible a la cultura. Observa los problemas cuando intento modificar la propiedad $watched dentro del propio método $watch:
$scope.$watch("property", function (newValue, oldValue) {
$scope.outputMessage = "oldValue: " + oldValue + " newValue: " + newValue;
$scope.property = Globalize.parseFloat(newValue);
});
(http://jsfiddle.net/gyZH8/2/)
El elemento de entrada se confunde mucho cuando el usuario comienza a escribir. Lo mejoré dividiendo la propiedad en dos propiedades, una para el valor sin parsear y otra para el valor parseado:
$scope.visibleProperty= 0.0;
$scope.hiddenProperty = 0.0;
$scope.$watch("visibleProperty", function (newValue, oldValue) {
$scope.outputMessage = "oldValue: " + oldValue + " newValue: " + newValue;
$scope.hiddenProperty = Globalize.parseFloat(newValue);
});
(http://jsfiddle.net/XkPNv/1/)
Esto fue una mejora sobre la primera versión, pero es un poco más verboso, y observa que todavía hay un problema con la propiedad parsedValue de los cambios de alcance (escribe algo en el segundo input, lo que cambia directamente el parsedValue. observa que el input superior no se actualiza). Esto podría ocurrir desde una acción del controlador o al cargar datos desde un servicio de datos.
¿Existe alguna manera más fácil de implementar este escenario usando AngularJS? ¿Me estoy perdiendo de alguna funcionalidad en la documentación?
Respuesta #1
Resulta que hay una solución muy elegante para esto, pero no está bien documentada.
El formateo de los valores del modelo para su visualización puede manejarse mediante el operador | y un formatter angular. Resulta que el ngModel tiene no solo una lista de formateadores sino también una lista de analizadores.
1. Use ng-model para crear el enlace de datos bidireccional
<input type="text" ng-model="foo.bar"></input>
2. Crea una directiva en tu módulo de Angular que se aplicará al mismo elemento y que dependa del controlador ngModel
module.directive('lowercase', function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attr, ngModel) {
...
}
};
});
3. Dentro del método link, añade tus convertidores personalizados al controlador ngModel
function fromUser(text) {
return (text || '').toUpperCase();
}
function toUser(text) {
return (text || '').toLowerCase();
}
ngModel.$parsers.push(fromUser);
ngModel.$formatters.push(toUser);
4. Agrega tu nueva directiva al mismo elemento que ya tiene el ngModel
<input type="text" lowercase ng-model="foo.bar"></input>
Aquí hay un ejemplo funcional que transforma el texto a minúsculas en el input y de nuevo a mayúsculas en el modelo
La Documentación de la API para el Controlador de Modelo también tiene una explicación breve y una visión general de los otros métodos disponibles.
