web-dev-qa-db-fra.com

filtres sur ng-model dans une entrée

J'ai une entrée de texte et je ne veux pas permettre aux utilisateurs d'utiliser des espaces, et tout ce qui est dactylographié sera transformé en minuscule.

Je sais que je ne suis pas autorisé à utiliser des filtres sur ng-model, par exemple.

ng-model='tags | lowercase | no_spaces'

J'ai envisagé de créer ma propre directive, mais l'ajout de fonctions à $parsers et $formatters n'a pas mis à jour l'entrée, mais uniquement aux autres éléments contenant ng-model.

Comment puis-je modifier l'entrée de celle que je tape actuellement?

J'essaie essentiellement de créer la fonctionnalité 'tags' qui fonctionne exactement comme celle de StackOverflow.

122
Andrew WC Brown

Je suggérerais de regarder la valeur du modèle et de la mettre à jour au prochain changement: http://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng?p=preview

Le seul problème intéressant concerne les espaces: Dans AngularJS 1.0.3, ng-model on input coupe automatiquement la chaîne. Par conséquent, il ne détecte pas que le modèle a été modifié si vous ajoutez des espaces à la fin ou au début (les espaces ne sont donc pas automatiquement supprimés par my. code). Mais dans 1.1.1 il y a une directive 'ng-trim' qui permet de désactiver cette fonctionnalité ( commit ). J'ai donc décidé d'utiliser la version 1.1.1 pour obtenir la fonctionnalité exacte que vous avez décrite dans votre question.

28
Valentyn Shybanov

Je crois que l'intention des entrées AngularJS et de la directive ngModel est que ne entrée non valide ne doit jamais se retrouver dans le modèle. Le modèle doit toujours être valide. Le problème avec un modèle non valide est que nous pouvons avoir des observateurs qui déclenchent et prennent des actions (inappropriées) basées sur un modèle non valide.

À mon avis, la solution appropriée consiste à se connecter au pipeline $parsers et à s’assurer que les entrées non valides ne sont pas intégrées au modèle. Je ne suis pas sûr de savoir comment avez-vous essayé d'aborder les choses ou ce qui n'a pas fonctionné pour vous avec $parsers mais voici une directive simple qui résout votre problème (ou du moins ma compréhension du problème):

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

Dès que la directive ci-dessus est déclarée, elle peut être utilisée comme suit:

<input ng-model="sth" ng-trim="false" custom-validation>

Comme dans la solution proposée par @Valentyn Shybanov, nous devons utiliser la directive ng-trim si nous voulons interdire les espaces au début/à la fin de l'entrée.

L'avantage de cette approche est double:

  • La valeur invalide n'est pas propagée au modèle
  • En utilisant une directive, il est facile d’ajouter cette validation personnalisée à n’importe quelle entrée sans dupliquer les observateurs encore et encore
203

Une solution à ce problème pourrait être d’appliquer les filtres du côté du contrôleur:

$scope.tags = $filter('lowercase')($scope.tags);

N'oubliez pas de déclarer $filter en tant que dépendance.

23

Si vous utilisez un champ de saisie en lecture seule, vous pouvez utiliser ng-value avec filter.

par exemple:

ng-value="price | number:8"
6
Edward D. Wilson

Utilisez une directive qui ajoute à la fois les collections $ formatters et $ parsers pour vous assurer que la transformation est effectuée dans les deux sens.

Voir cette autre réponse pour plus de détails, y compris un lien vers jsfiddle.

4
Scott Munro

J'ai eu un problème similaire et utilisé

ng-change="handler(objectInScope)" 

dans mon gestionnaire, j'appelle une méthode de objectInScope pour se modifier correctement (entrée grossière). Dans le contrôleur j'ai initié quelque part que

$scope.objectInScope = myObject; 

Je sais que cela n’utilise pas de filtres sophistiqués ni d’observateurs ... mais c’est simple et fonctionne très bien. Le seul inconvénient, c’est que l’objetInScope est envoyé dans l’appel au gestionnaire ...

3
wojjas

Si vous effectuez une validation complexe d’entrée asynchrone, il peut être intéressant d’abstraire ng-model jusqu’à un niveau dans le cadre d’une classe personnalisée avec ses propres méthodes de validation.

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>

code

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

})();
1
Daniel Lizik

Tu peux essayer ça

$scope.$watch('tags ',function(){

    $scope.tags = $filter('lowercase')($scope.tags);

});
0
Nikhil Mahirrao