AngularJS指令:模糊后不会触发ng-click

时间:2014-01-13 06:10:48

标签: angularjs angularjs-directive

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,如果您在电话号码末尾添加“a”,然后点击按钮,则不会调用doSomething()。如果再次单击该按钮,则会调用doSomething()

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

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

6 个答案:

答案 0 :(得分:9)

解释

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

    使用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-mousedown之前触发ng-blurng-blur回调将推迟到ng-mouseup被触发。这是通过利用$q服务来实现的。

答案 1 :(得分:4)

这是因为该指令正在将<span>Error</span>插入当前按钮所在的位置,从而干扰了click事件的位置。你可以通过移动文本框上方的按钮来看到这一点,一切都应该正常。

编辑:

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

答案 2 :(得分:4)

此处:http://jsbin.com/epEBECAy/25/edit

正如其他答案所解释的那样,在按钮上的onmouseup事件发生之前,按钮的外观会移动按钮,从而导致您遇到的问题。实现所需内容的最简单方法是对表单进行样式设置,使得跨度的外观不会导致按钮移动(从UX的角度来看,这是一个好主意)。您可以通过将input元素包装在div样式为white-space:nowrap的情况下执行此操作。只要跨度有足够的水平空间,按钮就不会移动,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 :(得分:1)

不是直接答案,而是以不同方式编写指令的建议(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
  };
}); 

答案 4 :(得分:0)

我建议在验证电话号码时使用$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/

答案 5 :(得分:0)

我用以下内容修复了它。

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