使用scope.$watch的Angular指令来强制验证其他字段

6
我编写了一个名为match-model的Angular指令,用于用户在我的应用程序中注册时进行密码/密码重复过程。密码重复字段具有特定属性,可以根据原始密码字段验证该字段。
为了优化目的,我的指令使用了scope.$watch。这样我就不必每次验证重复密码作用域属性时都读取相关作用域属性值,而是只需使用缓存的值,该值会在相关作用域属性值更改(即原始密码)时更改。
以下是我的指令:
.directive("matchModel", ["$timeout", function ($timeout) {
    return {
        require: "ngModel",
        link: function (scope, element, attributes, ngModelController) {

            var valueCache = null;

            // watch "match-model" model property value
            scope.$watch(attributes["matchModel"], function (newValue, oldValue) {
                valueCache = newValue;
                /*
                scope.$apply(); // error $digest in progress
                $timeout(function () { scope.$digest(); }); // no error, not working
                $timeout(function () { scope.$apply(); }); // no error, not working
                */
            });

            // add model validation parser
            ngModelController.$parsers.unshift(function (value) {
                ngModelController.$setValidity("match", value === valueCache);
                return value === valueCache ? value : undefined;
            });
        }
    };
}]);

我的表单包含两个字段(与此问题相关):

<input type="password" name="password" id="password" placeholder="password"
       class="validate" autocomplete="off"
       required
       ng-minlength="6"
       ng-model="data.password" />
<input type="password" name="passwordRepeat" id="passwordRepeat" placeholder="repeat password"
       class="validate" autocomplete="off"
       required
       ng-model="data.passwordRepeat"
       match-model="data.password" />

需求

  1. 当用户输入第一个密码时,只有在输入足够的字符(在此例中为6个字符)后,该字段才变为有效状态。
  2. 当用户输入第二个密码时,只有当数据与第一个密码匹配时,该字段才变为有效状态。
  3. 如果用户返回到第一个字段并更改原始密码,则第二个字段应失效。

目前的工作方式

1和2的表现符合预期,但3则不然。这就是我想要添加scope.$digest以传播范围模型更改到其他字段的原因。scope.$watch也是正确的时机,因为它在特定作用域的模型属性更改时执行。

似乎scope.$digest(或者同样适用于scope.$apply)不会验证模型。验证似乎不会随之执行。

问题

那么,我应该如何执行类似scope.$validate甚至更好的element.$validate,以便仅验证我的特定字段而不是整个模型(导致UI中的无效表单)?


听起来使用 ng-validate 并提供一个控制器函数可能更简单。 - Davin Tryon
对于那些感兴趣的人,我有一个类似但略有不同的案例/问题。这是我的解决方案:http://stackoverflow.com/questions/34850124/how-to-revalidate-a-form-with-multiple-dependent-fields/34871684 - Mathias Conradt
2个回答

3

我会通过在 $watch 中明确验证 $viewValue 来完成它:

PLUNKER

app.directive("matchModel", [
  function () {
    return {
      require: "ngModel",
      link: function (scope, element, attributes, ngModelController) {

        var valueCache = null;

        scope.$watch(attributes["matchModel"], function (newValue, oldValue) {
          valueCache = newValue;
          validate(ngModelController.$viewValue);
        });

        var validate = function (value) {
          ngModelController.$setValidity("match", value === valueCache);
          return value === valueCache ? value : undefined;
        };

        ngModelController.$parsers.unshift(validate);
      }
    };
}]);

1

如果我要这样做,我会只检查一个地方是否相等:

app.directive("matchModel", function () {
    return {
        require: "ngModel",
        link: function (scope, element, attributes, ngModelController) {

            scope.$watch(function(){
               return scope.$eval(attributes["matchModel"]) == ngModelController.$viewValue; //only 1 place to check if they are equal
            }, function (newValue) {
                ngModelController.$setValidity("match", newValue);
            });

        }
    };
});

DEMO

可以翻译为:

{{链接1:演示}}


您的演示不起作用。即使我在两个字段中输入相同的密码,这些字段仍然保持为红色/无效。 - Mathias Conradt
1
@Mathias Conradt:您至少需要输入6个字符。 代码中有ng-minlength="6"。 我同意错误指示器并不是那么具有信息性,但这不是问题的主要焦点,这很容易更改。 - Khanh TO
谢谢,我忘记了最小长度。 - Mathias Conradt

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