【问题标题】:AngularJS directives: ng-click is not triggered after blurAngularJS 指令:模糊后不触发 ng-click
【发布时间】:2014-01-31 19:46:01
【问题描述】:

DEMO

考虑以下示例:

<input type="text" ng-model="client.phoneNumber" phone-number>
<button ng-click="doSomething()">Do Something</button>
.directive("phoneNumber", function($compile) {
  return {
    restrict: 'A',
    scope: true,
    link: function(scope, element, attrs) { 
      scope.mobileNumberIsValid = true;

      var errorTemplate = "<span ng-show='!mobileNumberIsValid'>Error</span>";

      element.after($compile(errorTemplate)(scope)).on('blur', function() {
        scope.$apply(function() { 
          scope.mobileNumberIsValid = /^\d*$/.test(element.val());
        });
      });
    }  
  };
});

查看demo,如果您在电话号码末尾添加say 'a',然后单击按钮,则不会呼叫doSomething()。如果再次单击该按钮,则会调用 doSomething()

为什么第一次没有调用doSomething()?任何想法如何解决这个问题?

注意:保持验证模糊很重要。

【问题讨论】:

  • 这个怎么样? #wrapper {width: 220px;}。我认为最好保持你的设计保持原样,尽管动态因素,即错误消息,被添加和删除。不过这是你的电话。
  • 在使用按钮的同时移动按钮是非常糟糕的 UI 设计。当出现错误时,我会将跨度留在那里并将空白 (&amp;nbsp;) 文本更改为“错误”。

标签: angularjs angularjs-directive


【解决方案1】:

我用以下方法修复了它。

<button class="submitButton form-control" type="submit" ng-mousedown="this.form.submit();" >

【讨论】:

    【解决方案2】:

    这里:http://jsbin.com/epEBECAy/25/edit

    正如其他答案所解释的,在按钮上的 onmouseup 事件发生之前,按钮被 span 的外观移动,从而导致您遇到的问题。完成您想要的最简单的方法是设置表单样式,使跨度的外观不会导致按钮移动(从用户体验的角度来看,这通常是一个好主意)。您可以通过将input 元素包装在div 中来实现这一点,其样式为white-space:nowrap。只要span有足够的水平空间,按钮就不会移动,ng-click事件会按预期工作。

    <div id="wrapper">
        <div style='white-space:nowrap;'>
            <input type="text" ng-model="client.phoneNumber" phone-number>
        </div>
        <button ng-click="doSomething()">Do Something</button>
    </div>
    

    【讨论】:

      【解决方案3】:

      我建议在验证电话号码时使用$parsers$setValidity 方式。

      app.directive('phoneNumber', function () {
          return {
              restrict: 'A',
              require: '?ngModel',
              link: function(scope, element, attrs, ctrl) { 
                  if (!ctrl) return;
                  ctrl.$parsers.unshift(function(viewValue) {
                      var valid = /^\d*$/.test(viewValue);
                      ctrl.$setValidity('phoneNumber', valid);
                      return viewValue;
                  });
                  ctrl.$formatters.unshift(function(modelValue) {
                      var valid = /^\d*$/.test(modelValue);
                      ctrl.$setValidity('phoneNumber', valid);
                      return modelValue;
                  });
      
              }
          }
      });
      

      因此,您将能够在视图中的字段上使用 $valid 属性:

      <form name="form" ng-submit="doSomething()" novalidate>
          <input type="text" name="phone" ng-model="phoneNumber" phone-number/>
          <p ng-show="form.phone.$invalid">(Show on error)Wrong phone number</p>
      </form>
      

      如果您只想在 blur 上显示错误,您可以使用(可在此处找到:AngularJS Forms - Validate Fields After User Has Left Field):

      var focusDirective = function () {
        return {
          restrict: 'E',
          require: '?ngModel',
          link: function (scope, element, attrs, ctrl) {
            var elm = $(element);
            if (!ctrl) return;
            elm.on('focus', function () {
              elm.addClass('has-focus');
              ctrl.$hasFocus = true;
              if(!scope.$$phase) scope.$digest();
            });
            elm.on('blur', function () {
              elm.removeClass('has-focus');
              elm.addClass('has-visited');
              ctrl.$hasFocus = false;
              ctrl.$hasVisited = true;
              if(!scope.$$phase) scope.$digest();
            });
            elm.closest('form').on('submit', function () {
              elm.addClass('has-visited');
              ctrl.$hasFocus = false;
              ctrl.$hasVisited = true;
              if(!scope.$$phase) scope.$digest();
            })
          }
        }   
      };
      app.directive('input', focusDirective);
      

      因此,如果字段现在聚焦,您将拥有 hasFocus 属性,如果该字段模糊一次或多次,您将拥有 hasVisited 属性:

      <form name="form" ng-submit="doSomething()" novalidate>
          <input type="text" name="phone" ng-model="phoneNumber" phone-number/>
          <p ng-show="form.phone.$invalid">[error] Wrong phone number</p>
          <p ng-show="form.phone.$invalid 
                   && form.phone.$hasVisited">[error && visited] Wrong phone number</p>
          <p ng-show="form.phone.$invalid 
                  && !form.phone.$hasFocus">[error && blur] Wrong phone number</p>
          <div><input type="submit" value="Submit"/></div>
      </form>
      

      演示: http://jsfiddle.net/zVpWh/4/

      【讨论】:

        【解决方案4】:

        解释

        1. 使用点击按钮,按钮元素触发mousedown事件。
        2. 输入在blur,触发模糊回调以验证输入值。
        3. 如果无效,则显示错误范围,按下按钮标签,即光标左按钮区域。如果用户释放鼠标,mouseup 事件不会被触发。这就像单击一个按钮,但在释放鼠标以取消按钮单击之前移出它。这就是ng-click 未被触发的原因。因为按钮上没有触发mouseup 事件。

        解决方案

        使用ng-pattern动态验证输入值,并根据ngModel.$invalid属性立即显示/隐藏错误范围。

        演示 1 http://jsbin.com/epEBECAy/14/edit

        -- 更新 1 -----

        根据作者的要求,用另一种解决方案更新了答案。

        演示 2 http://jsbin.com/epEBECAy/21/edit?html,js

        HTML

        <body ng-app="Demo" ng-controller="DemoCtrl as demoCtrl">
          <pre>{{ client | json }}</pre>
          <div id="wrapper">
            <input type="text" phone-number
                   ng-model="client.phoneNumber"
                   ng-blur="demoCtrl.validateInput(client.phoneNumber)">
          </div>
          <button ng-mousedown="demoCtrl.pauseValidation()" 
                  ng-mouseup="demoCtrl.resumeValidation()" 
                  ng-click="doSomething()">Do Something</button>
        </body>
        

        逻辑

        我在输入上使用了ng-blur 指令来触发验证。如果在ng-blur 之前触发ng-mousedown,则ng-blur 回调将延迟到ng-mouseup 被触发。这是通过使用$q 服务来完成的。

        【讨论】:

        • 整个想法是对模糊进行验证,而不是在每次按键时进行验证。当用户刚开始输入电话号码时显示错误时,我觉得有点烦人(例如,如果我们验证电话号码正好有 10 位数字)。
        • @MishaMoroshko 请参阅更新 1。在更新 1 中,元素布局与之前相同,但仍会在 blur 上触发验证。
        • 我认为 AngularJS 是自以为是的框架,ng-pattern 背后应该有逻辑原因
        • 太棒了,谢谢。所以基本上,在 ng-mousedown 上,我注意到按下了保存按钮。 Onblur,如果设置了该标志,我会记下它。当验证完成(异步)时,我调用 save 方法。跛脚。
        【解决方案5】:

        这是因为指令在当前放置按钮的下方插入&lt;span&gt;Error&lt;/span&gt;,干扰了点击事件的位置。您可以通过移动文本框上方的按钮来看到这一点,一切都应该正常。

        编辑:

        如果您确实必须在同一位置出现错误,并且在不创建自己的点击指令的情况下解决问题,您可以使用ng-mousedown 而不是ng-click。这将在处理模糊事件之前触发点击代码。

        【讨论】:

        • 任何想法如何在不移动按钮的情况下解决这个问题?
        • 这有点小技巧,但你可以在很短的 $timeout 内包装 scope.$apply。它只需要比一般的点击速度长。否则,您可能必须创建自己的 ng-click 指令来克服位置问题。
        • @MishaMoroshko:我想知道 什么是理想的?要么必须移动某些东西(输入、跨度或按钮),要么您可以在按钮上设置一个完美的解决方法(这也将非常“非 Angular-ish”。什么
        • 添加了另一个简单的解决方案。
        • 从 UX 设计来看,那里的错误永远不是 ideal。我想知道为什么@MishaMoroshko 不使用ng-pattern
        【解决方案6】:

        不是直接回答,而是建议以不同方式编写指令(html相同):

        http://jsbin.com/OTELeFe/1/

        angular.module("Demo", [])
        .controller("DemoCtrl", function($scope) {
          $scope.client = {
            phoneNumber: '0451785986'
          };
          $scope.doSomething = function() {
            console.log('Doing...');
          };
        })
        .directive("phoneNumber", function($compile) {
        
          var errorTemplate = "<span ng-show='!mobileNumberIsValid'> Error </span>";
        
           var link = function(scope, element, attrs) { 
        
            $compile(element.find("span"))(scope);
        
            scope.mobileNumberIsValid = true;
            scope.$watch('ngModel', function(v){
             scope.mobileNumberIsValid = /^\d*$/.test(v);
            });
          }; 
        
          var compile = function(element, attrs){
              var h = element[0].outerHTML;
              var newHtml = [
                '<div>',
                h.replace('phone-number', ''),
                errorTemplate,
                '</div>'
              ].join("\n");
              element.replaceWith(newHtml);
              return link;
            };
        
          return {
            scope: {
              ngModel: '='
            },
            compile: compile
          };
        }); 
        

        【讨论】:

        • 整个想法是对模糊进行验证!
        猜你喜欢
        • 1970-01-01
        • 2017-11-01
        • 2016-11-13
        • 1970-01-01
        • 2018-09-07
        • 1970-01-01
        • 1970-01-01
        • 2013-03-14
        • 1970-01-01
        相关资源
        最近更新 更多