Angularjs 一次发送一封电子邮件进行表单验证

Angularjs 一次发送一封电子邮件进行表单验证,angularjs,angularjs-directive,Angularjs,Angularjs Directive,我正在尝试为电子邮件字段实现表单验证。验证应执行以下操作: 检查是否已通过所需属性输入电子邮件,如果未输入电子邮件,则显示消息 检查电子邮件是否具有有效的格式(似乎是在不指定属性的情况下自动完成的),如果格式错误,则显示消息 检查该电子邮件在$http.get调用上是否唯一,并显示一条消息,以防找到该电子邮件,因此无法使用 如果字段为空,则显示第一条消息;如果电子邮件无效,则显示第二条消息;如果找到电子邮件地址,则显示第三条消息,因此每次不显示唯一的电子邮件地址 如果我只尝试使用“requi

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

  • 检查是否已通过所需属性输入电子邮件,如果未输入电子邮件,则显示消息
  • 检查电子邮件是否具有有效的格式(似乎是在不指定属性的情况下自动完成的),如果格式错误,则显示消息
  • 检查该电子邮件在$http.get调用上是否唯一,并显示一条消息,以防找到该电子邮件,因此无法使用
如果字段为空,则显示第一条消息;如果电子邮件无效,则显示第二条消息;如果找到电子邮件地址,则显示第三条消息,因此每次不显示唯一的电子邮件地址

如果我只尝试使用“required”属性,但一旦我添加email available directive属性,它就不再检查电子邮件的格式,email available directive将与required属性一起执行。两条消息都会弹出,但我希望用户一次只能看到一条消息

我使用的是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>
<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>

我通过删除必需的属性并添加第二个名为email valid的指令解决了这个问题。此外,我还删除了emailAvailable指令的else子句中的setValidity方法

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>
<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>

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

1) 确保在内置角度验证器之后运行验证器所需的全部操作是将其推送到
ctrl.$parsers
上,而不是将其推到
unshift()

2) 由于先前运行的验证程序显示验证程序无效(即,如果字段已无效,您不想进行Ajax调用),因此阻止验证程序运行。您只需要在验证器内部的
if
语句中检查
ctrl.$invalid

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

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

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

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

7) 旁注:在您的回答中,我注意到您在ajax响应处理程序中返回值。。。那对你没有任何帮助。因为调用是异步的,所以实际上总是从解析器返回未定义的。它会更新你的模型吗?如果真是这样,我会很惊讶

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

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

        }
    };
});

我对Ben Lesh answer进行了一点扩展,添加了一个超时,这样它就不会检查每一次击键

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

我已经添加了一个答案。因为当你的答案有效时(也许,看看我的答案)。我看到了一些缺陷。感谢布莱什,这真的有助于理解。啊,啊,这解释了很多事情。因此,默认验证程序方法驻留在$parser中,如果我将验证方法取消移动到数组的开头,而不是将其推到数组的结尾,那么默认验证方法将被我的方法推翻。你也在我脑海中打开了一些结角验证是如何真正工作的。你真的是通过阅读angular文档发现这一点的吗?或者你有其他来源吗?:)无论如何,谢谢你,你的详细解释和plunker示例让我成为一个更快乐的angularjs用户。我通读了他们的文章。;)然后是一些尝试和错误。我还写了一个。我看到的第二个令人毛骨悚然的答案,第二次给我留下了深刻的印象!如果问题是javascript,则应使用javascript进行回答!