фільтри на ng-моделі на вході


124

У мене є введення тексту, і я не хочу дозволити користувачам використовувати пробіли, і все набране буде перетворено на малі літери.

Я знаю, що мені заборонено використовувати фільтри на ng-моделі, наприклад.

ng-model='tags | lowercase | no_spaces'

Я дивився на створення власної директиви, але додавав функції $parsersта $formattersне оновлював вхід, лише інші елементи, які були ng-modelна ньому.

Як я можу змінити дані, які я зараз набираю?

Я по суті намагаюся створити функцію 'теги', яка працює так само, як та, яка знаходиться тут у StackOverflow.


Дивіться , якщо з допомогою $ таймаут (..., 0) з нг-зміна допомагає: stackoverflow.com/questions/12176925 / ...
Марк Rajcok

Відповіді:


28

Я б запропонував переглянути цінність моделі та оновити її після зміни: http://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng?p=preview

Єдине цікаве питання з пробілами: У AngularJS 1.0.3 ng-модель при введенні автоматично обрізає рядок, тому вона не виявляє, що модель була змінена, якщо ви додаєте пробіли в кінці або на початку (тому пробіли не видаляються автоматично моїми код). Але в 1.1.1 є директива 'ng-trim', яка дозволяє відключити цю функціональність ( фіксація ). Тому я вирішив використовувати 1.1.1 для досягнення точної функціональності, яку ви описали у своєму запитанні.


Це саме те, що я шукав. Виявляється, я вже використовую angularjs 1.1.1
Andrew WC Brown

@Valentyn, ваше рішення застосовано до питання SO, на який я посилався у коментарі вище. Дякую. stackoverflow.com/questions/12176925 / ...
Відзначити Rajcok

це рішення може мати погані побічні ефекти, дивіться іншу відповідь нижче, для цього слід використовувати директиву
pilavdzice

Переназначення змінної області зсередини $watchпримушує слухача знову викликати. У простих випадках (коли ваш фільтр ідентичний) ви в кінцевому підсумку виконаєте фільтр двічі за кожну модифікацію.
втілився

204

Я вважаю, що задум AngularJS та ngModelдиректива полягає в тому, що недійсний вхід ніколи не повинен опинятися в моделі . Модель завжди повинна бути дійсною. Проблема з невірною моделлю полягає в тому, що у нас можуть бути спостерігачі, які стріляють і вживають (невідповідні) дії на основі недійсної моделі.

Як я бачу, правильне рішення тут - підключитись до $parsersконвеєра та переконатися, що неправильний вхід не входить у модель. Я не впевнений, як ти намагався підійти до речей або з чим саме не спрацював, $parsersале ось проста директива, яка вирішує вашу проблему (або, принаймні, моє розуміння проблеми):

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

Як тільки вищевказана директива оголошена, її можна використовувати так:

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

Як і у рішенні, запропонованому @Valentyn Shybanov, нам потрібно використовувати ng-trimдирективу, якщо ми хочемо заборонити пробіли на початку / кінці введення.

Перевага такого підходу - дворазова:

  • Недійсне значення не поширюється на модель
  • Використовуючи директиву, легко додати цю власну перевірку на будь-який вхід, не копіюючи спостерігачів знову і знову

1
Я впевнений, що складною частиною з modelCtrl.$setViewValue(transformedInput); modelCtrl.$render();Корисним було б посилання на документацію: docs.angularjs.org/api/ng.directive:ngModel.NgModelController Одне слово "захистити" моє рішення - це те, що властивість області можна змінити не лише з поглядів та мій спосіб прикрити це. Тому я думаю, що від фактичної ситуації залежить те, як сферу застосування можна змінити.
Валентин Шибанов

2
на що у вашому прикладі позначається "modelCtrl"?
GSto

4
Звідки ти береш inputValue?
Дофс

2
@GSto modelCtrl- контролер, необхідний директиві. ( require 'ngModel')
Нейт-Вілкінс

7
Курсор стрибає до кінця текстового поля щоразу, коли ви вводите недійсний символ, спробуйте написати "world" та змінити його на "HeLLo world"!
Хафез Дівандарі

23

Рішенням цієї проблеми може бути застосування фільтрів на стороні контролера:

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

Не забудьте заявити $filterпро залежність.


4
Але вам знадобиться $-годинник, якщо ви хочете, щоб оновлено належним чином.
Містер Міккель

це виконується лише один раз. і додавання до годинника не є правильним рішенням, оскільки це, навіть спочатку, дозволяє моделі стати недійсною - правильне рішення - додати до аналізаторів $ модель.
icfantv

4
Не потрібно сподобатися моїй відповіді, але це не означає, що це неправильно. Перевірте своє его перед тим, як звернути увагу.
icfantv

6

Якщо ви використовуєте поле лише для читання, ви можете використовувати ng-значення з фільтром.

наприклад:

ng-value="price | number:8"

4

Використовуйте директиву, яка додає до форматів $ і колекції $ parsers, щоб переконатися, що перетворення здійснюється в обох напрямках.

Дивіться цю іншу відповідь для отримання більш детальної інформації, включаючи посилання на jsfiddle.


3

У мене була схожа проблема і була використана

ng-change="handler(objectInScope)" 

у своєму оброблювачі я викликаю метод objectInScope, щоб правильно модифікувати себе (грубий вхід). У контролері я десь ініціював це

$scope.objectInScope = myObject; 

Я знаю, що це не використовує жодних вигадливих фільтрів чи годинників ... але це просто і чудово працює. Єдиним недоліком цього є те, що objectInScope надсилається у виклику обробнику ...


1

Якщо ви робите складну перевірку введення асинхронізації, можливо, варто її абстрагувати ng-modelодин рівень як частину спеціального класу зі своїми власними методами перевірки.

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>

код

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

})();

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.