在输入框中使用ng-model筛选器

127

我有一个文本输入框,不希望用户使用空格,并且所有键入的内容都会转换为小写字母。

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

ng-model='tags | lowercase | no_spaces'

我考虑创建自己的指令,但将函数添加到$parsers$formatters中并没有更新输入,只有其他具有ng-model的元素更新了。

我该如何更改当前正在输入的输入内容?

我实际上正在尝试创建像StackOverflow这里的“标签”功能。


尝试使用 $timeout(...,0) 和 ng-change 一起使用:https://dev59.com/Dmct5IYBdhLWcg3wUb5- - Mark Rajcok
9个回答

207

我认为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
在你的示例中,'modelCtrl' 指的是什么? - GSto
4
你从哪里获取inputValue的值? - Dofs
2
@GSto modelCtrl 是指令所需的控制器。(require 'ngModel') - Nate-Wilkins
7
每次输入无效字符时,光标都会跳到文本框的末尾。请尝试输入“world”,并将其修改为“HeLLo world”! - Hafez Divandari
1
抱歉在两年后回答这个问题,但是你也可以在link()函数中设置attrs.ngTrim = 'false',从而避免需要将属性添加到元素中。看起来它必须是一个字符串,而不是布尔值。 - Geuis
显示剩余9条评论

29

建议观察模型值并在更改时更新它:http://plnkr.co/edit/Mb0uRyIIv1eK8nTg3Qng?p=preview

唯一需要注意的问题是空格:在AngularJS 1.0.3中,input上的ng-model自动修剪字符串,因此如果您在末尾或开头添加空格,则不会检测到模型已更改(因此我的代码不会自动删除空格)。但是在1.1.1中,有一个'ng-trim'指令,允许禁用此功能(提交)。所以我决定使用1.1.1来实现您在问题中描述的确切功能。


这正是我正在寻找的。结果发现我已经在使用angularjs 1.1.1了。 - Andrew WC Brown
@Valentyn,你的解决方案应用于我在上面评论中提到的SO问题。谢谢。https://dev59.com/Dmct5IYBdhLWcg3wUb5- - Mark Rajcok
这个解决方案可能会有不良副作用,请参见下面的其他答案,你应该使用指令来解决。 - pilavdzice
$watch 中重新分配作用域变量会强制再次调用监听器。在简单情况下(其中您的过滤器是幂等的),每次修改都会导致过滤器执行两次。 - BorisOkunskiy

23

解决这个问题的方法是在控制器端应用过滤器:

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

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


4
如果你想让它正确更新,你需要在它上面放置一个“$watch”。 - Mr Mikkél
这只会执行一次。将其添加到监视器中并不是正确的解决方案,因为即使最初,它也会导致模型无效 - 正确的解决方案是将其添加到模型的$ parsers中。 - icfantv
4
你不必喜欢我的回答,但这并不意味着它是错误的。在你给负评前,请先检查一下自己的自尊心。 - icfantv

10
如果您正在使用只读输入字段,您可以使用带有过滤器的ng-value。
例如:
ng-value="price | number:8"

1
+1 这可能不是对所问问题的确切回答,但包含它对那些情况略有不同的人是有帮助的。 - ksadowski

5
使用指令将内容添加到$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) })
  };

})();

0
你可以尝试这个。
$scope.$watch('tags ',function(){

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

});

0

我来这里是寻找一个解决方案,可以在我们输入时主动修改输入文本并用*代替除了最后4位之外的所有内容。这可以通过$formatters实现。

例如:账户号码输入框:1234567890AHSB1应该在输入框中显示为**********AHSB

答案只是稍微变化了一下 @pkozlowski.opensource 给出的解决方案。

angular.module('myApp').directive('npiMask', function() {
  return {
    require: 'ngModel',
    link: function($scope, element, attrs, modelCtrl) {
      modelCtrl.$formatters.push(function(inputValue) {
        var transformedInput = inputValue.toString().replace(/.(?=.{4,}$)/g, '*');
        if (transformedInput !== inputValue) {
          modelCtrl.$setViewValue(transformedInput);
          modelCtrl.$render();
        }
        return transformedInput;
      });
    }
  };
});
<input-text 
 name="accountNum" 
 label="{{'LOAN_REPAY.ADD_LOAN.ACCOUNT_NUM_LABEL' | translate}}" 
 ng-model="vm.model.formData.loanDetails.accountNum" 
 is-required="true" 
 maxlength="35" 
 size="4" 
 npi-mask>
</input-text>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接