angularjs - filtrar - filtros en ng-model en una entrada
ng model search (8)
Creo que la intención de las entradas de AngularJS y ngModel
direcive es que la entrada no válida nunca debería terminar en el modelo . El modelo siempre debe ser válido. El problema de tener un modelo no válido es que podríamos tener vigilantes que disparan y toman acciones (inapropiadas) basadas en un modelo no válido.
Como lo veo, la solución adecuada aquí es conectarlo a la tubería $parsers
y asegurarse de que la entrada no válida no entre en el modelo. No estoy seguro de cómo trataste de abordar las cosas o qué exactamente no funcionó para ti con $parsers
pero aquí hay una directiva simple que resuelve tu problema (o al menos mi comprensión del problema):
app.directive(''customValidation'', function(){
return {
require: ''ngModel'',
link: function(scope, element, attrs, modelCtrl) {
modelCtrl.$parsers.push(function (inputValue) {
var transformedInput = inputValue.toLowerCase().replace(/ /g, '''');
if (transformedInput!=inputValue) {
modelCtrl.$setViewValue(transformedInput);
modelCtrl.$render();
}
return transformedInput;
});
}
};
});
Tan pronto como se declare la directiva anterior, se puede usar así:
<input ng-model="sth" ng-trim="false" custom-validation>
Como en la solución propuesta por @Valentyn Shybanov, necesitamos usar la directiva ng-trim
si queremos rechazar espacios al comienzo / final de la entrada.
La ventaja de este enfoque es doble:
- El valor inválido no se propaga al modelo
- Usando una directiva es fácil agregar esta validación personalizada a cualquier entrada sin duplicar a los observadores una y otra vez
Tengo una entrada de texto y no quiero permitir que los usuarios usen espacios, y todo lo que se escribe se convertirá en minúsculas.
Sé que no puedo usar filtros en ng-model, por ejemplo.
ng-model=''tags | lowercase | no_spaces''
Consideré crear mi propia directiva, pero al agregar funciones a $parsers
y $formatters
no actualicé la entrada, solo otros elementos que tenían ng-model
en ella.
¿Cómo puedo cambiar la entrada de la que estoy escribiendo actualmente?
Básicamente, estoy intentando crear la función ''etiquetas'' que funciona igual que la de StackOverflow.
Puedes intentar esto
$scope.$watch(''tags '',function(){
$scope.tags = $filter(''lowercase'')($scope.tags);
});
Si está utilizando un campo de entrada de solo lectura, puede usar ng-value con filter.
por ejemplo:
ng-value="price | number:8"
Si realiza una validación de entrada asíncrona compleja, puede valer la pena abstraer el ng-model
en un nivel como parte de una clase personalizada con sus propios métodos de validación.
https://plnkr.co/edit/gUnUjs0qHQwkq2vPZlpO?p=preview
html
<div>
<label for="a">input a</label>
<input
ng-class="{''is-valid'': vm.store.a.isValid == true, ''is-invalid'': vm.store.a.isValid == false}"
ng-keyup="vm.store.a.validate([''isEmpty''])"
ng-model="vm.store.a.model"
placeholder="{{vm.store.a.isValid === false ? vm.store.a.warning : ''''}}"
id="a" />
<label for="b">input b</label>
<input
ng-class="{''is-valid'': vm.store.b.isValid == true, ''is-invalid'': vm.store.b.isValid == false}"
ng-keyup="vm.store.b.validate([''isEmpty''])"
ng-model="vm.store.b.model"
placeholder="{{vm.store.b.isValid === false ? vm.store.b.warning : ''''}}"
id="b" />
</div>
código
(function() {
const _ = window._;
angular
.module(''app'', [])
.directive(''componentLayout'', layout)
.controller(''Layout'', [''Validator'', Layout])
.factory(''Validator'', function() { return Validator; });
/** Layout controller */
function Layout(Validator) {
this.store = {
a: new Validator({title: ''input a''}),
b: new Validator({title: ''input b''})
};
}
/** layout directive */
function layout() {
return {
restrict: ''EA'',
templateUrl: ''layout.html'',
controller: ''Layout'',
controllerAs: ''vm'',
bindToController: true
};
}
/** Validator factory */
function Validator(config) {
this.model = null;
this.isValid = null;
this.title = config.title;
}
Validator.prototype.isEmpty = function(checkName) {
return new Promise((resolve, reject) => {
if (/^/s+$/.test(this.model) || this.model.length === 0) {
this.isValid = false;
this.warning = `${this.title} cannot be empty`;
reject(_.merge(this, {test: checkName}));
}
else {
this.isValid = true;
resolve(_.merge(this, {test: checkName}));
}
});
};
/**
* @memberof Validator
* @param {array} checks - array of strings, must match defined Validator class methods
*/
Validator.prototype.validate = function(checks) {
Promise
.all(checks.map(check => this[check](check)))
.then(res => { console.log(''pass'', res) })
.catch(e => { console.log(''fail'', e) })
};
})();
Sugeriría ver el valor del modelo y actualizarlo después de chage: http://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng?p=preview
El único problema interesante es con los espacios: en AngularJS 1.0.3 ng-model on input recorta automáticamente la cadena, por lo que no detecta que el modelo se modificó si agrega espacios al final o al inicio (para que los espacios no se eliminen automáticamente) código). Pero en 1.1.1 hay una directiva ''ng-trim'' que permite desactivar esta funcionalidad ( commit ). Así que decidí usar 1.1.1 para lograr la funcionalidad exacta que describiste en tu pregunta.
Tuve un problema similar y utilicé
ng-change="handler(objectInScope)"
en mi controlador llamo a un método de objectInScope para que se modifique correctamente (entrada aproximada). En el controlador, he iniciado algo que
$scope.objectInScope = myObject;
Sé que esto no usa ningún filtro o observador sofisticado ... pero es simple y funciona muy bien. El único inconveniente de esto es que el objectInScope se envía en la llamada al controlador ...
Una solución a este problema podría ser aplicar los filtros en el lado del controlador:
$scope.tags = $filter(''lowercase'')($scope.tags);
No olvides declarar $filter
como dependencia.
Use una directiva que se sume a las colecciones $ formateadores y $ analizadores para garantizar que la transformación se realice en ambas direcciones.
Consulte esta otra respuesta para obtener más información, incluido un enlace a jsfiddle.