filtra su ng-model in un input


124

Ho un input di testo e non voglio consentire agli utenti di utilizzare gli spazi e tutto ciò che viene digitato verrà trasformato in minuscolo.

So che non mi è permesso usare i filtri su ng-model ad es.

ng-model='tags | lowercase | no_spaces'

Ho cercato di creare la mia direttiva ma aggiungendo funzioni $parserse $formattersnon ho aggiornato l'input, solo altri elementi che avevano ng-modelsu di esso.

Come posso modificare l'input di cui sto scrivendo?

Sto essenzialmente cercando di creare la funzione 'tag' che funziona esattamente come quella qui su StackOverflow.


Vedere se si utilizza $ timeout (..., 0) con NG-cambio aiuta: stackoverflow.com/questions/12176925/...
Mark Rajcok

Risposte:


28

Suggerirei di guardare il valore del modello e aggiornarlo su chage: http://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng?p=preview

L'unico problema interessante riguarda gli spazi: in AngularJS 1.0.3 ng-model sull'input taglia automaticamente la stringa, quindi non rileva che il modello è stato modificato se aggiungi spazi alla fine o all'inizio (quindi gli spazi non vengono rimossi automaticamente dal mio codice). Ma in 1.1.1 esiste la direttiva 'ng-trim' che consente di disabilitare questa funzionalità ( commit ). Quindi ho deciso di utilizzare 1.1.1 per ottenere la funzionalità esatta che hai descritto nella tua domanda.


Questo era esattamente quello che cercavo. Si scopre che sto già usando angularjs 1.1.1
Andrew WC Brown

@Valentyn, la tua soluzione applicata alla domanda SO a cui ho fatto riferimento nel commento sopra. Grazie. stackoverflow.com/questions/12176925/...
Mark Rajcok

questa soluzione può avere effetti collaterali negativi, vedi altre risposte di seguito, dovresti usare una direttiva per questo
pilavdzice,

La riassegnazione della variabile dell'ambito dall'interno $watchforza il richiamo del listener. In casi semplici (in cui il filtro è idempotente), il filtro verrà eseguito due volte su ogni modifica.
incarnato il

204

Credo che l'intenzione degli input di AngularJS e della ngModeldirettiva sia che l' input non valido non dovrebbe mai finire nel modello . Il modello dovrebbe essere sempre valido. Il problema con avere un modello non valido è che potremmo avere degli osservatori che sparano e intraprendono azioni (inadeguate) basate su un modello non valido.

A mio avviso, la soluzione corretta qui è quella di collegarsi alla $parserspipeline e assicurarsi che l'input non valido non entri nel modello. Non sono sicuro di come hai provato ad avvicinarti alle cose o con cosa esattamente non ha funzionato per te, $parsersma ecco una semplice direttiva che risolve il tuo problema (o almeno la mia comprensione 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;         
       });
     }
   };
});

Non appena viene dichiarata la suddetta direttiva, può essere utilizzata in questo modo:

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

Come nella soluzione proposta da @Valentyn Shybanov, dobbiamo usare la ng-trimdirettiva se vogliamo vietare gli spazi all'inizio / alla fine dell'input.

Il vantaggio di questo approccio è duplice:

  • Il valore non valido non viene propagato al modello
  • Utilizzando una direttiva è facile aggiungere questa convalida personalizzata a qualsiasi input senza duplicare ripetutamente gli osservatori

1
Sono sicuro che la parte difficile con modelCtrl.$setViewValue(transformedInput); modelCtrl.$render();Utile sarebbe il collegamento alla documentazione: docs.angularjs.org/api/ng.directive:ngModel.NgModelController Una parola per "proteggere" la mia solitudine è che la proprietà dell'ambito potrebbe essere cambiata non solo dalle viste e il mio modo di coprire questo. Quindi penso che dipenda da una situazione reale come il campo di applicazione potrebbe essere modificato.
Valentyn Shybanov,

2
a cosa si riferisce "modelCtrl" nel tuo esempio?
GSto

4
Da dove prendi inputValue?
Dofs,

2
@GSto modelCtrlè il controller richiesto dalla direttiva. ( require 'ngModel')
Nate-Wilkins

7
Il cursore salta alla fine del campo di testo ogni volta che digiti un carattere non valido, prova a scrivere "mondo" e modificalo in "mondo HeLLo"!
Hafez Divandari,

23

Una soluzione a questo problema potrebbe essere quella di applicare i filtri sul lato controller:

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

Non dimenticare di dichiarare $filtercome dipendenza.


4
Ma avresti bisogno di un $ watch su di esso se vuoi che si aggiorni correttamente.
Mikkél,

questo viene eseguito solo una volta. e l'aggiunta a un orologio non è la soluzione giusta perché, anche inizialmente, consente al modello di diventare non valido: la soluzione corretta è aggiungere ai $ parser del modello.
Icfantv,

4
Non devi apprezzare la mia risposta, ma ciò non significa che sia sbagliato. Controlla il tuo ego prima di votare.
Icfantv,

6

Se si utilizza il campo di input di sola lettura, è possibile utilizzare ng-value con filtro.

per esempio:

ng-value="price | number:8"

4

Utilizzare una direttiva che si aggiunge alle raccolte $ formatters e $ parser per assicurarsi che la trasformazione venga eseguita in entrambe le direzioni.

Vedi questa altra risposta per maggiori dettagli incluso un link a jsfiddle.


3

Ho avuto un problema simile e usato

ng-change="handler(objectInScope)" 

nel mio gestore chiamo un metodo di objectInScope per modificarlo correttamente (input approssimativo). Nel controller ho avviato da qualche parte quello

$scope.objectInScope = myObject; 

So che questo non usa filtri o osservatori fantasiosi ... ma è semplice e funziona alla grande. L'unico lato negativo di questo è che objectInScope viene inviato nella chiamata al gestore ...


1

Se stai eseguendo una convalida di input complessa e asincrona, varrebbe la pena astrarre ng-modelun livello come parte di una classe personalizzata con i propri metodi di convalida.

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>

codice

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

})();

0

Puoi provare questo

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

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

});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.