фільтри на 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.