Angularjs在同一元素的指令中修改ng-model,并进行验证

Angularjs change ng-model within a directive on the same element with validations

本文关键字:ng-model 验证 修改 元素 指令 Angularjs      更新时间:2023-09-26

在最原始的angular应用中,我试图为一个输入字段创建一个指令,它可以改变父节点的ng-model值。

HTML:

<form novalidate>
  <input ng-model="ctrl.myvalue" mydirective minlength="19" />
  {{ ctrl.myvalue }}
</form>

JS:

var app = angular.module('app', []);
app.directive('mydirective', function(){
    return {
        scope: { ngModel: '=' },
        link: function(scope, el) {
           el.on('input', function(e) {
              this.value = this.value.replace(/ /g,'');
              scope.ngModel = this.value;
           })
        }
    }
})
app.controller('MyController', function(){
  this.myvalue = '';
})

砰砰作响

问题是,如果我使用这个指令与minlengthpattern一起输入验证,它会得到一个特定的行为:每第二个字母你输入消失;ng-model也得到undefined值。没有验证,代码可以完美地工作。

我也试图创建一个自定义验证作为一个解决方案,但它有相同的效果。

你能解释一下或建议怎么走吗?

您可以在第一次迭代中使用unsift,以及render。通常你可以使用ctrl.$setViewValue,但你可以确保当值不改变时不重新启动…

var testModule = angular.module('myModule', []);
testModule.controller('testCntrl', ['$scope', function ($scope) {
    $scope.test = 'sdfsd  fsdf sdfsd sdf';
}]);
testModule.directive('cleanSpaces', [function () {
    return {
        require: '?ngModel',
        link: function (scope, $elem, attrs, ctrl) {
            if (!ctrl) return;
            var filterSpaces = function (str) {
                return str.replace(/ /g, '');
            }
            ctrl.$parsers.unshift(function (viewValue) {
                var elem = $elem[0],
                    pos = elem.selectionStart,
                    value = '';
                if (pos !== viewValue.length) {
                    var valueInit = filterSpaces(
                    viewValue.substring(0, elem.selectionStart));
                    pos = valueInit.length;
                }
                //I launch the regular expression, 
                // maybe you prefer parse the rest 
                // of the substring and concat.
                value = filterSpaces(viewValue);
                $elem.val(value);
                elem.setSelectionRange(pos, pos);
                ctrl.$setViewValue(value);
                return value;
            });
            ctrl.$render = function () {
                if (ctrl.$viewValue) {
                    ctrl.$setViewValue(filterSpaces(ctrl.$viewValue));
                }
            };
        }
    };
}]);
http://jsfiddle.net/luarmr/m4dmz0tn/

UPDATE我用最后的代码和angular中的验证示例更新了fiddle,并用ng-trim (ngModel)更新了html。

使用Angular的NgModelController。我只是添加到$解析器(在视图更新时执行的函数,但在值被持久化到模型之前)。这里,我将函数推入$parsers管道。请记住,在最小长度验证得到满足之前,不会填充模型。代码片段显示了$viewValue和modelValue

var app = angular.module('app', []);
app.directive('mydirective', function() {
  return {
    require: 'ngModel',
    priority: 100,
    link: function(scope, el, attrs, ngModelCtrl) {
      // $parsers from view/DOM to model
      ngModelCtrl.$parsers.push(function(value) {
        console.log(value);
        return value && value.replace(/ /g, '');
      });
    }
  }
})
app.controller('MyController', function() {
  this.myvalue = '';
})
<script src="https://code.angularjs.org/1.4.0/angular.min.js"></script>
<div ng-app="app" ng-controller="MyController as ctrl">
  <form name="myForm" novalidate>
    <input ng-model="ctrl.myvalue" name="myValue" mydirective minlength="19" /><br /><br />Model Value: {{ ctrl.myvalue }}<br /><br />
    View Value: {{ myForm.myValue.$viewValue }}
  </form>
</div>

Update:如果您正在尝试执行自定义验证,请忘记最小长度/必需的东西,只需编写自己的内容。在用户键入时更改文本可能不是最好的行为。这个例子将在模糊事件的viewValue中添加空格。我仍然认为ngModelController是要走的路,但我不知道你想要完成什么,给你更接近你正在寻找的东西。

var app = angular.module('app', []);
app.directive('creditCardValidator', function() {
  return {
    require: 'ngModel',
    priority: 100,
    link: function(scope, el, attrs, ngModelCtrl) {
      // 16 characters
      attrs.$set('maxlength', 16);
      var noSpaces = function noSpaces(value) {
        return value.replace(/ /g, '');
      }
      var withSpaces = function withSpaces(value) {
        if (ngModelCtrl.$isEmpty(value)) {
          return;
        }
        var spacedValue = value.replace(/('d{4})('d{4})('d{4})('d{4})/, '$1 $2 $3 $4');
        return spacedValue || undefined;
      }
      ngModelCtrl.$parsers.push(noSpaces);
      ngModelCtrl.$formatters.push(withSpaces);
      ngModelCtrl.$validators.validCreditCard = function(modelValue, viewValue) {
        var value = noSpaces(modelValue || viewValue);
        var valid = /^'d{16}$/.test(value);
        return valid;
      };
      el.on('blur', function() {
        if (ngModelCtrl.$valid) {
          ngModelCtrl.$setViewValue(withSpaces(ngModelCtrl.$modelValue));
          ngModelCtrl.$render();
        }
      });
    }
  }
})
app.controller('MyController', function() {
  this.myvalue = '';
})
<script src="https://code.angularjs.org/1.4.0/angular.min.js"></script>
<div ng-app="app" ng-controller="MyController as ctrl">
  <form name="myForm" novalidate>
    <input ng-model="ctrl.myvalue" name="myValue" ng-model-options="{allowInvalid: true}" credit-card-validator />
    <br />
    <br />Model Value: {{ ctrl.myvalue }}
    <br />
    <br />View Value: {{ myForm.myValue.$viewValue }}
    <br />
    <br />Error: {{ myForm.myValue.$error }}
  </form>
</div>