7

我正在尝试为电子邮件字段实施表单验证。验证应执行以下操作:

  • 检查是否已通过必需属性输入了电子邮件,如果未输入电子邮件,则显示一条消息
  • 检查电子邮件是否具有有效的格式(似乎是在没有指定属性的情况下自动完成的)并在格式错误时显示消息
  • 通过 $http.get 调用检查电子邮件是否是唯一的,并显示一条消息,以防找到电子邮件并因此无法使用

我希望出现第一条消息,如果该字段为空,如果电子邮件无效,则出现第二条消息,如果找到电子邮件地址,则出现第三条消息,因此一次不是唯一的。

如果我仅尝试使用“必需”属性,则此方法有效,但是一旦我添加了电子邮件可用指令属性,它就不再检查电子邮件的格式,并且电子邮件可用指令与必需属性一起执行。两条消息都会弹出,但我只希望用户一次看到一条消息。

我正在使用 angularjs 1.1.3。

有人可以告诉我我可能做错了什么吗?

HTML

<div id="user_mod" class="mod_form" ng-show="userModScreenIsVisible">
<form name="mod_user" novalidate>
    <input type="email" name="email" ng-model="user.email" placeholder="E-Mail" required email-available/>
    <div class="user-help" ng-show="mod_user.email.$dirty && mod_user.email.$invalid">Invalid:
        <span ng-show="mod_user.email.$error.required">Please enter your email.</span>
        <span ng-show="mod_user.email.$error.email">This is not a valid email.</span>
        <span ng-show="mod_user.email.$error.emailAvailable">This email address is already taken.</span>
    </div>
</form>

指示

directive('emailAvailable', function($http) { // available
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
            ctrl.$parsers.unshift(function(viewValue) {
                ctrl.$setValidity('emailAvailable', false);
                if(viewValue !== "" && typeof viewValue !== "undefined") {
                    console.log("variable is defined");

                    $http.get('/api/user/email/' + viewValue + '/available')
                        .success(function(data, status, headers, config) {
                            console.log(status);
                            ctrl.$setValidity('emailAvailable', true);
                            return viewValue;
                        })
                        .error(function(data, status, headers, config) {
                            console.log("error");
                            ctrl.$setValidity('emailAvailable', false);
                            return undefined;
                        });
                } else {
                    console.log("variable is undefined");
                    ctrl.setValidity('emailAvailable', false);
                    return undefined;
                }
            });
        }
    };
});
4

3 回答 3

19

我看到您已经解决了自己的问题,但我想我可以在这里为您提供一些提示/建议(我希望):

1) 你需要做的就是确保你的验证器是在内置的角度验证器运行之后运行的,而不是它。push()ctrl.$parsersunshift()

2) 阻止您的验证器运行,因为之前运行的验证器显示它无效(即,如果您不想在该字段已经无效的情况下进行 Ajax 调用)。你只需要在你的验证器中签ctrl.$invalid入一个语句。if

3)您需要$setValidity()在开始您的ajax调用之前和收到它之后单独调用来使您的表单无效。这样,在 AJAX 返回并说明它是否有效之前,您的表单是无效的。

4) 这可能是次要的,但除非您也添加 a,否则ctrl.$formatter最初分配给 $scope 中对象的值在写入屏幕之前不会被验证。如果您的表单是通过路由、ng-repeat 或 ng-include 动态添加到屏幕并预先填充了数据,这可能是一个问题。通常所有的验证器都应该有一个 $parser 组件(视图 -> 模型)和一个 $formatter 组件(模型 -> 视图)。

5) 一个警告。如果该值无效,大多数验证器都会从模型中完全删除该值。因为您正在进行异步调用,所以您必须立即在解析器函数中返回 viewValue。通常,如果字段无效,解析器函数将返回undefined,从而防止无效数据出现在您的模型中。

6) 由于验证器在 $error 对象中维护了一个状态,因此您需要在最初命中此异步验证器时将其清除。见下文。

7)旁注:在你的回答中,我注意到你在你的ajax响应处理程序中返回值......这不会为你做任何事情。因为调用是异步的,所以您实际上总是从该解析器返回 undefined。它会更新您的模型吗?如果确实如此,我会感到惊讶。

以下是我如何更改您的原始指令以使其按您可能希望的方式工作:

app.directive('emailAvailable', function($http, $timeout) { // available
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
          console.log(ctrl);
            // push the validator on so it runs last.
            ctrl.$parsers.push(function(viewValue) {
                // set it to true here, otherwise it will not 
                // clear out when previous validators fail.
                ctrl.$setValidity('emailAvailable', true);
                if(ctrl.$valid) {
                  // set it to false here, because if we need to check 
                  // the validity of the email, it's invalid until the 
                  // AJAX responds.
                  ctrl.$setValidity('checkingEmail', false);

                  // now do your thing, chicken wing.
                  if(viewValue !== "" && typeof viewValue !== "undefined") {
                      $http.get('/api/user/email/' + viewValue + '/available')
                          .success(function(data, status, headers, config) {
                              ctrl.$setValidity('emailAvailable', true);
                              ctrl.$setValidity('checkingEmail', true);
                          })
                          .error(function(data, status, headers, config) {
                              ctrl.$setValidity('emailAvailable', false);
                              ctrl.$setValidity('checkingEmail', true);
                          });
                  } else {
                      ctrl.$setValidity('emailAvailable', false);
                      ctrl.$setValidity('checkingEmail', true);
                  }
                }
                return viewValue;
            });

        }
    };
});

而且...当然,这是一个证明这一切的笨蛋

于 2013-03-24T01:52:23.037 回答
2

我已经通过删除必需的属性并添加第二个名为 email-valid 的指令来解决它。我还删除了 emailAvailable 指令的 else 子句中的 setValidity 方法。

HTML

<form name="mod_user" novalidate>
    <input type="email" name="email" ng-model="user.email" placeholder="E-Mail" email-valid email-available/>
    <div class="user-help" ng-show="mod_user.email.$dirty && mod_user.email.$invalid">Invalid:
        <span ng-show="mod_user.email.$error.emailValid">Please enter a valid email address.</span>
        <span ng-show="mod_user.email.$error.emailAvailable">This email address is already taken.</span>
    </div>
    <br/>
    <button class="button" ng-click="hideUserModScreen()">Close</button>
    <button class="button" ng-click="updateUserDetails()" ng-disabled="mod_user.$invalid" ng-show="updateUserDetailsButtonIsVisible">Update</button>
    <button class="button" ng-click="saveUserDetails()" ng-disabled="mod_user.$invalid" ng-show="saveUserDetailsButtonIsVisible">Save</button>
</form>

指令

angular.module('myApp.directives', []).
directive('appVersion', ['version', function (version) {
    return function (scope, elm, attrs) {
        elm.text(version);
    };
}]).

directive('emailAvailable', function($http) { // available
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
            ctrl.$parsers.unshift(function(viewValue) {
                if(viewValue && viewValue.match(/[a-z0-9\-_]+@[a-z0-9\-_]+\.[a-z0-9\-_]{2,}/)) {
                    console.log("variable is defined");

                    $http.get('/api/user/email/' + viewValue + '/available')
                        .success(function(data, status, headers, config) {
                            console.log(status);
                            ctrl.$setValidity('emailAvailable', true);
                            return viewValue;
                        })
                        .error(function(data, status, headers, config) {
                            console.log("error");
                            ctrl.$setValidity('emailAvailable', false);
                            return undefined;
                        });
                } else {
                    console.log("variable is undefined");
                    return undefined;
                }
            });
        }
    };
}).

directive('emailValid', function() {
    return {
        require: 'ngModel',
        link: function(scope, elm, attrs, ctrl) {
            ctrl.$parsers.unshift(function(viewValue) {
                if (viewValue && viewValue.match(/[a-z0-9\-_]+@[a-z0-9\-_]+\.[a-z0-9\-_]{2,}/)) {
                    // it is valid
                    ctrl.$setValidity('emailValid', true);
                    return viewValue;
                } else {
                    // it is invalid, return undefined (no model update)
                    ctrl.$setValidity('emailValid', false);
                    return undefined;
                }
            });
        }
    };
});
于 2013-03-23T21:50:40.887 回答
0

我通过添加超时来扩展 Ben Lesh 的答案,这样它就不会检查每次击键

app.directive "uniqueEmail", ($http, $timeout) ->
    restrict: "A" 
    require: "ngModel" 
    link: (scope, elem, attrs, ctrl) ->
        return unless ctrl 
        q = null
        ctrl.$parsers.push (viewValue) ->
            ctrl.$setValidity 'unique-email', true
            if ctrl.$valid
                if viewValue?
                    $timeout.cancel(q) if q?
                    q = $timeout (->
                        ctrl.$setValidity 'checking-unique-email', false
                        $http.get("#{endpoint}/emails/#{viewValue}/exists").success (exists) ->
                            ctrl.$setValidity 'checking-unique-email', true
                            ctrl.$setValidity 'unique-email', exists is 'false'
                        q = null
                        ) , attrs.checkDelay ? 1000

            viewValue
于 2014-05-22T21:04:09.827 回答