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.
mousedown
event is triggered on button element.blur
, blur callback triggered to validate input value.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.Use ng-pattern
to dynamically validate the input value, and show/hide error span immediately according to ngModel.$invalid
property.
According to author's request, updated answer with another solution.
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>
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.
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>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With