在输入中对ng-model进行过滤


124

我有一个文本输入,不想让用户使用空格,所有键入的内容都将变为小写。

我知道我不允许在ng-model上使用过滤器。

ng-model='tags | lowercase | no_spaces'

我研究了创建自己的指令,但仅向其上添加的元素添加了功能$parsers$formatters但未更新输入ng-model

如何更改当前输入的内容?

我实质上是在尝试创建“标签”功能,该功能与StackOverflow上的功能相同。


看看使用$超时(...,0)和NG-变化帮助: stackoverflow.com/questions/12176925/...
马克Rajcok

Answers:


28

我建议您观看模型值并在更改时对其进行更新:http ://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng? p= preview

唯一有趣的问题是空格:在AngularJS 1.0.3 ng-model中,输入会自动修剪字符串,因此,如果在结尾或开头添加空格,它不会检测到模型已更改(因此,空格不会自动删除)码)。但是在1.1.1中,有一个“ ng-trim”指令允许禁用此功能(commit)。因此,我决定使用1.1.1来实现您在问题中描述的确切功能。


这正是我想要的。事实证明,我已经在使用angularjs 1.1.1
Andrew WC Brown

@Valentyn,您的解决方案适用于我在上面的注释中引用的SO问题。谢谢。 stackoverflow.com/questions/12176925/...
马克Rajcok

此解决方案可能会有不良的副作用,请参见下面的其他答案,您应为此使用指令
pilavdzice 2014年

从内部重新分配范围变量将$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如果我们要在输入的开头/结尾不允许空格,则需要使用指令。

这种方法的优点是2倍:

  • 无效值不会传播到模型
  • 使用指令可以轻松地将此自定义验证添加到任何输入,而无需一遍又一遍地重复观察者

1
我敢肯定,modelCtrl.$setViewValue(transformedInput); modelCtrl.$render();有用的部分是有用的,它将链接到文档:docs.angularjs.org/api/ng.directive : ngModel.NgModelController 一个“保护”我的话的意思是,不仅可以从视图和我的方式掩盖了这一点。因此,我认为这取决于实际情况,如何修改范围。
Valentyn Shybanov 2013年

2
在您的示例中,“ modelCtrl”指的是什么?
GSto

4
您从哪里获得inputValue?
Dofs

2
@GSto modelCtrl是指令所需的控制器。(require 'ngModel'
Nate-Wilkins 2014年

7
每次您键入无效字符时,光标会跳到文本字段的末尾,尝试编写“ world”并将其修改为“ Hello world”!
哈菲兹·迪万达里

23

解决此问题的方法可能是在控制器端应用过滤器:

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

不要忘记声明$filter为依赖项。


4
但是,如果您希望它正确更新,则需要一个$ watch。
MIKKEL先生

这仅执行一次。并且添加到手表中不是正确的解决方案,因为即使在最初,它也允许模型变为无效-正确的解决方案是将模型添加到$ parsers中。
icfantv '16

4
您不必喜欢我的答案,但这并不意味着它是错误的。拒绝投票之前先检查一下自我。
icfantv '16

6

如果您使用的是只读输入字段,则可以将ng-value与filter一起使用。

例如:

ng-value="price | number:8"

4

使用可同时添加到$ formatters和$ 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) })
  };

})();

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.