编译元素导致输入插入位置移动到结束

时间:2014-02-19 21:54:12

标签: angularjs angularjs-directive

我遇到指令问题。该指令的目的是轻松添加验证,而无需手动将ng-class(以及其他内容)添加到元素中,以便显示错误状态。我只是想在我的元素上放置一个“validation”指令,并在出现错误状态时生成相应的类(和错误消息)。

就验证而言,它运作良好,但它会产生奇怪的副作用。每当我在输入框中编辑一个具有验证指令的值时,它就会将插入符号移动到输入字段中文本的末尾。似乎是我正在编译元素(在这种情况下是包含该元素的父元素)。

Here is a jsbin显示问题。要重现,请在字段中键入值,然后将插入符号放在刚刚键入的值的中间,然后尝试键入另一个字符。请注意,它会将您移动到字段的末尾。请注意,如果删除该值,则字段标签将按预期变为红色以显示验证错误(该字段是必需的)。

这是指令(来自jsbin):

angular.module('app', [])
.directive('validation', function($compile) {
  return {
    require: 'ngModel',
    restrict: 'A',
    compile: function(compileElement, attrs) {
      var formName = compileElement[0].form.name;
      compileElement.removeAttr('validation');
      compileElement.parent().attr('ng-class', formName + "['" + attrs.name + "'].$invalid && " + formName + "['" + attrs.name + "'].$dirty ? 'error' : ''");

      return function(scope, element) {
        $compile(element.parent())(scope);
      }
    }
  };
});

这是html:

<html>
  <head>
  <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.1/angular.min.js"></script>
  </head>
  <body ng-app="app">
    <form name="subscribeForm">
    <label>
      First Name
      <input type="text" 
             id="firstName" 
             name="firstName" 
             ng-model="userInfo.FirstName" 
             required 
             validation/>
    </label>
    </form>
  </body>
</html>

2 个答案:

答案 0 :(得分:1)

不确定你是否已经解决了这个问题,但我遇到了类似的问题。在Preserving cursor position with angularjs找到了解决方案。为方便起见,下面是解决此问题的指令代码段。

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;
      });
    }
  }
});

该指令有不同的用途,但请根据您的要求进行修改。

答案 1 :(得分:0)

如果您没有使用内置验证模型/流程,那么您做错了。查看angular-js网站上的教程:

http://code.angularjs.org/1.2.13/docs/guide/forms

此外,您不应该在编译阶段进行元素操作。

更新

您需要查看名为自定义验证的部分。

使用ctrl.$parsers方法。您将解析器添加到解析器列表中,并且您的fn将在模型更改时运行。然后使用ctrl.$setValidity('strNameOfValidation', true)设置有效性。然后,Angular将为您添加一个类 - 名为.ng-valid-float.ng-invalid-float

var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;
app.directive('smartFloat', function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) {
      ctrl.$parsers.unshift(function(viewValue) {
        if (FLOAT_REGEXP.test(viewValue)) {
          ctrl.$setValidity('float', true);
          return parseFloat(viewValue.replace(',', '.'));
        } else {
          ctrl.$setValidity('float', false);
          return undefined;
        }
      });
    }
  };
});