使用angularjs保留光标位置

时间:2014-04-08 14:39:03

标签: jquery angularjs validation

以下代码段执行我想要的input,即删除所有非字母数字字符,转换为大写,并保留光标位置。

element = $(element);

element.keyup(function() {
    var x = element.val();
    var y = x && x.toUpperCase().replace(/[^A-Z\d]/g, '');
    if (x===y) return;
    var start = this.selectionStart;
    var end = this.selectionEnd + y.length - x.length;
    element.val(y);
    this.setSelectionRange(start, end);
});

我将此代码段放在指令的link中,并且大部分都有效。

问题是angular模型在应用更改之前看到值。我尝试了Google如何使用$apply$digest或其他任何内容,但没有任何效果。

(实际上,我以某种方式管理它,但随后内容被重新渲染,我失去了位置。我无法重现它,但它还不够好,无论如何。)

2 个答案:

答案 0 :(得分:21)

这样做的方法

  • 输入仅清理一次
  • 输入上的
  • ngChange只会被触发一次

是使用ngModelController提供的$parsers数组。它被设计为影响模型值的地方(通过其返回值),但它也可以用作输入事件的监听器。

app.directive('cleanInput', function() {
  return {
    require: 'ngModel',
    link: function(scope, element, attrs, ngModelController) {
      var el = element[0];

      function clean(x) {
        return x && x.toUpperCase().replace(/[^A-Z\d]/g, '');
      }

      ngModelController.$parsers.push(function(val) {
        var cleaned = clean(val);

        // Avoid infinite loop of $setViewValue <-> $parsers
        if (cleaned === val) return val;

        var start = el.selectionStart;
        var end = el.selectionEnd + cleaned.length - val.length;

        // element.val(cleaned) does not behave with
        // repeated invalid elements
        ngModelController.$setViewValue(cleaned);
        ngModelController.$render();

        el.setSelectionRange(start, end);
        return cleaned;
      });
    }
  }
});

但是,我不确定$parsers的这种用法是否有点像黑客。该指令可用作:

<input type="text" clean-input ng-model="name">

或者如果您想要ngChange功能:

<input type="text" clean-input ng-model="name" ng-change="onChange()">

这可以在http://plnkr.co/edit/dAJ46XmmC49wqTgdp2qz?p=preview

中看到

答案 1 :(得分:1)

所需要的主要内容是:

  • 要求ngModelController能够调用其方法并获取/设置其值。具体地说...

  • 将来电element.val(y)替换为

    ngModelController.$setViewValue(y);
    ngModelController.$render();
    

    我认为我应该承认,我不完全确定ngModelController的内部运作方式,以了解为什么这是必要的。

  • 可选,但可以通过element.val()获取视图中的现有值:

    ngModelController.$viewValue;
    

    至少与设置视图值的方式更加一致。

  • 再次是可选的,但是听input事件使得界面更好一点,因为它似乎在keyup事件之前触发了一点,所以你没有得到闪光未处理的输入。

  • 添加到$parsers数组以处理输入似乎会停止为未处理的输入版本触发任何ngChange回调。

    ngModelController.$parsers.push(function(val) {
      // Return the processed value
    })
    

将所有这些放在一起作为自定义指令:

app.directive('cleanInput', function() {
  return {
    require: 'ngModel',
    link: function(scope, element, attrs, ngModelController) {
      function clean(x) {
        return x && x.toUpperCase().replace(/[^A-Z\d]/g, '');
      }

      ngModelController.$parsers.push(function(val) {
        return clean(val);
      })

      element.on('input', function() {
        var x = ngModelController.$viewValue;
        var y = clean(x);

        var start = this.selectionStart;
        var end = this.selectionEnd + y.length - x.length;

        ngModelController.$setViewValue(y);
        ngModelController.$render();
        this.setSelectionRange(start, end);
      });
    }
  }
});

可以用作:

<input type="text" clean-input ng-model="name">

或者如果您想要ngChange功能:

<input type="text" clean-input ng-model="name ng-change="onChange()">

并在http://plnkr.co/edit/FymZ8QEKwj2xXTmaExrH?p=preview

的行动中看到了

编辑:添加关于$parsers数组的部分。我应该承认,正是@ Engineer的答案让我想到了它。