Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS directives: ng-click is not triggered after blur

DEMO

Consider the following example:

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

Looking at the demo, if you add say 'a' at the end of the phone number, and click the button, doSomething() is not called. If you click the button again, then doSomething() is called.

Why doSomething() is not called for the first time? Any ideas how to fix this?

Note: It is important to keep the validation on blur.

like image 346
Misha Moroshko Avatar asked Jan 13 '14 06:01

Misha Moroshko


3 Answers

Explain

  1. Use click button, mousedown event is triggered on button element.
  2. Input is on blur, blur callback triggered to validate input value.
  3. If invalid, error span is displayed, pushing button tag down, thus cursor left button area. If user release mouse, mouseup event is not triggered. This acts like click on a button but move outside of it before releasing mouse to cancel the button click. This is the reason ng-click is not triggered. Because mouseup event is not triggered on button.

Solution

Use ng-pattern to dynamically validate the input value, and show/hide error span immediately according to ngModel.$invalid property.

Demo 1 http://jsbin.com/epEBECAy/14/edit

----- Update 1 -----

According to author's request, updated answer with another solution.

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

Logic

I used ng-blur directive on input to trigger validation. If ng-mousedown is triggered before ng-blur, ng-blur callback will be deferred until ng-mouseup is fired. This is accomplished by utilizing $q service.

like image 163
Daiwei Avatar answered Nov 09 '22 03:11

Daiwei


Here: http://jsbin.com/epEBECAy/25/edit

As explained by other answers, the button is moved by the appearance of the span before an onmouseup event on the button occurs, thus causing the issue you are experiencing. The easiest way to accomplish what you want is to style the form in such a way that the appearance of the span does not cause the button to move (this is a good idea in general from a UX perspective). You can do this by wrapping the input element in a div with a style of white-space:nowrap. As long as there is enough horizontal space for the span, the button will not move and the ng-click event will work as expected.

<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>
like image 42
Trevor Avatar answered Nov 09 '22 04:11

Trevor


It is because the directive is inserting the <span>Error</span> underneath where the button is currently placed, interfering with the click event location. You can see this by moving the button above the text box, and everything should work fine.

EDIT:

If you really must have the error in the same position, and solve the issue without creating your own click directive, you can use ng-mousedown instead of ng-click. This will trigger the click code before handling the blur event.

like image 22
Matt Way Avatar answered Nov 09 '22 05:11

Matt Way