【问题标题】:Preserving cursor position with angularjs使用angularjs保留光标位置
【发布时间】:2014-04-08 14:39:02
【问题描述】:

以下 sn-p 对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);
});

我将这个 sn-p 放在指令的 link 中,它可以正常工作.... 大部分情况下。

问题在于angular 模型在应用更改之前看到了值。我试图谷歌如何使用$apply$digest 或其他任何东西,但没有任何效果。

(其实我是有办法做到的,但是后来内容被重新渲染了,我失去了位置。我无法重现它,但它还是不够好。)

【问题讨论】:

    标签: jquery angularjs validation


    【解决方案1】:

    这样做的方法

    • 输入只被清理一次
    • 然后输入上的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看到在行动中

    【讨论】:

    • 很好的解决方案,谢谢!我尝试设置插入符号位置的所有其他方式导致插入符号来回跳跃,您的解决方案非常完美
    • @michal-charemza 可以维护不同的modelValue和viewValue。例如,我想像 XXX-XXX-XXXX 这样动态格式化输入,但模型应该只包含 XXXXXXXXXX。
    【解决方案2】:

    需要的主要有:

    • 要求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 的回答让我想到了它。

    【讨论】:

    • 这似乎工作正常,只是感觉不对:我需要清洁两次,一次在oninput,一次在$parsers
    • 我同意感觉不对。试图说服自己/自己没关系,我意识到 ngModelController 控制/与两件事交互。模型值和视图值。因此,每个清洁输入一次可以被视为可以。但是,我现在想到了一种清洁过一次的替代方法,但不确定它是否有点落后。将作为单独的答案发布。
    猜你喜欢
    • 2014-07-18
    • 1970-01-01
    • 2015-11-13
    • 1970-01-01
    • 1970-01-01
    • 2015-07-16
    • 1970-01-01
    • 2015-11-19
    • 1970-01-01
    相关资源
    最近更新 更多