Директивы AngularJS: ng-click не запускается после размытия
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());
});
});
}
};
});
Глядя на демонстрацию , если вы добавите say 'a' в конце номера телефона и нажмите кнопку doSomething()
не называется. Если вы снова нажмете кнопку, вызывается doSomething()
.
Почему doSomething()
не вызывается в первый раз? Любые идеи, как это исправить?
Примечание. Важно соблюдать валидацию при размытии.
Ответы
Ответ 1
Здесь: 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>
Ответ 2
Объяснить
- Используйте кнопку щелчка, событие
mousedown
запускается на элементе кнопки.
- Вход включен
blur
, срабатывает обратный вызов размытия для проверки входного значения.
- Если недействительно, отображается диапазон ошибок, нажатие кнопки тега вниз, таким образом, область левой кнопки курсора. Если пользователь освобождает мышь, событие
mouseup
не запускается. Это действует как кнопка на кнопке, но перемещается за ее пределы, прежде чем отпустить кнопку мыши, чтобы отменить нажатие кнопки. По этой причине ng-click
не запускается. Поскольку mouseup
событие не запускается на кнопке.
Решение
Используйте ng-pattern
для динамической проверки входного значения и отображения/скрытия диапазона ошибок сразу в соответствии с свойством ngModel.$invalid
.
----- Обновление 1 -----
В соответствии с запросом автора обновленный ответ с другим решением.
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-blur
, обратный вызов ng-blur
будет отложен до тех пор, пока ng-mouseup
не будет запущен. Это достигается за счет использования службы $q
.
Ответ 3
Это потому, что директива вставляет <span>Error</span>
под которым находится кнопка в настоящее время, мешая месту события клика. Вы можете увидеть это, перемещая кнопку над текстовым полем, и все должно работать нормально.
EDIT:
Если вы действительно должны иметь ошибку в той же позиции и решить проблему, не создавая собственную директиву кликов, вы можете использовать ng-mousedown
вместо ng-click
. Это вызовет код щелчка перед обработкой события размытия.
Ответ 4
Не прямой ответ, но предложение для написания директивы по-другому (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
};
});
Ответ 5
Я бы предложил использовать $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 - проверять поля после того, как пользователь имеет левое поле):
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/
Ответ 6
Я установил его следующим образом.
<button class="submitButton form-control" type="submit" ng-mousedown="this.form.submit();" >