Question:
There was a problem creating a datepicker
for a date field. There are 2 handlers on the field – blur
and focus
, in focus
a datepicker
is created, in blur
it is deleted. Moreover, in focus
it is created using a deferred object.
.directive('datePicker', [ '$compile', '$document', 'datePickerService', '$templateRequest', function ($compile, $document, datePickerService, $templateRequest) {
function link(scope, element, attrs) {
var picker = null;
var $elementFocusProcessing = $.Deferred();
element.on('focus', function (event) {
populatelimits();
prepareViewData();
var pos = angular.extend(element.offset(), { height: element[0].offsetHeight });
$templateRequest(datePickerService.datePickerTemplate).then(function (html) {
var template = angular.element(html);
$document.find('body').append(template);
picker = $compile(template)(scope);
picker.css({ top: pos.top + pos.height + 5, left: pos.left, display: 'block', position: 'absolute' });
picker.on('mousedown', function (evt) {
evt.preventDefault();
});
$elementFocusProcessing.resolve();
});
});
element.on('blur', function () {
$elementFocusProcessing.then(function () {
if (picker) {
picker.remove();
picker = null;
};
$elementFocusProcessing = $.Deferred();
});
});
}
return {
restrict: 'A',
link: link,
scope : {
model: '=ngModel'
}
};
And the actual problem: if you select the date field, then switch to another window, and then click on any place on the page, except for the date field, several events occur in a row: focus
fields, blur
fields, click
on the page. In this case, the creation of the picker
in focus
completes later than the attempt to remove it in blur
, and it is not removed, although the focus of the element in the toga is lost. How to make code execution in blur
happen only after code execution in focus
is completed? So far it has only occurred to me to make an additional focusEnded
variable to determine whether the code is being executed in focus
, and to organize a cycle with a timeout in blur
, but I feel that this can be done more elegantly using deferred objects.
Answer:
Since there is no working example, we can only reason theoretically:
-
Since the template does not change, it does not need to be loaded not only for each focus, but also for each link. Therefore, it can be taken out of the function and the result, and it is a promise , can be stored in a variable:
var templatePromise = $templateRequest(datePickerService.datePickerTemplate);
-
Next, the link function, inside it we mean that the template has already been loaded, for this we do everything else in the success handler of the then function of our saved
templatePromise
function link(scope,element,attrs){ templatePromise.then(function(html){ //достаточно сделать всего один элемент, // который мы будем добавлять или удалять var template = angular.element(html); //и добавим событие, чтобы не добавлять его каждый раз //так как template это уже объект jqLite можно сделать так template.on('mousedown', function (evt) { evt.preventDefault(); } ); //добавляем on focus element.on('focus', function (event) { ... $document.find('body').append(template); $compile(template)(scope); //так как template это объект jqLite, то вместо picker можно использовать его template.css(...) ... }); //добавляем on blur element.on('blur', function () { //так как добавляли template, то и удалять можно его //а так как он у нас всегда есть, то не надо проверять на null template.remove(); }); }); }