Как создать директиву обложки angularJs для yi-bootstrap datepicker?
Я использую директиву ui.bootstrap.datepicker для отображения некоторого поля даты. Однако большую часть времени мне нужна такая же настройка: я хочу, чтобы она появилась вместе с всплывающим окном и кнопкой всплывающего окна, а также я хочу, чтобы немецкие имена тексты. Это снова и снова создает тот же код для кнопки и текстов и форматирования, поэтому я написал свою собственную директиву, чтобы я не повторял себя.
Вот планком с моей директивой. Однако я, кажется, делаю это неправильно. Если вы выбираете дату с помощью выбора даты, используя параметр даты "date 1", который не использует мою директиву, все работает нормально.
Я бы ожидал то же самое для Date 2, но вместо отображения даты в соответствии с шаблоном, который я предоставил в поле ввода (или любым другим ожидаемым значением), он отображает представление .toString()
объекта даты (например, Fri Apr 03 2015 00:00:00 GMT+0200 (CEST)
).
Вот моя директива:
angular.module('ui.bootstrap.demo').directive('myDatepicker', function($compile) {
var controllerName = 'dateEditCtrl';
return {
restrict: 'A',
require: '?ngModel',
scope: true,
link: function(scope, element) {
var wrapper = angular.element(
'<div class="input-group">' +
'<span class="input-group-btn">' +
'<button type="button" class="btn btn-default" ng-click="' + controllerName + '.openPopup($event)"><i class="glyphicon glyphicon-calendar"></i></button>' +
'</span>' +
'</div>');
function setAttributeIfNotExists(name, value) {
var oldValue = element.attr(name);
if (!angular.isDefined(oldValue) || oldValue === false) {
element.attr(name, value);
}
}
setAttributeIfNotExists('type', 'text');
setAttributeIfNotExists('is-open', controllerName + '.popupOpen');
setAttributeIfNotExists('datepicker-popup', 'dd.MM.yyyy');
setAttributeIfNotExists('close-text', 'Schließen');
setAttributeIfNotExists('clear-text', 'Löschen');
setAttributeIfNotExists('current-text', 'Heute');
element.addClass('form-control');
element.removeAttr('my-datepicker');
element.after(wrapper);
wrapper.prepend(element);
$compile(wrapper)(scope);
scope.$on('$destroy', function () {
wrapper.after(element);
wrapper.remove();
});
},
controller: function() {
this.popupOpen = false;
this.openPopup = function($event) {
$event.preventDefault();
$event.stopPropagation();
this.popupOpen = true;
};
},
controllerAs: controllerName
};
});
И как я его использую:
<input my-datepicker="" type="text" ng-model="container.two" id="myDP" />
(Концепция была вдохновлена этим ответом)
Я использую angular 1.3 (plunker находится на 1.2, потому что я просто разблокировал плункер из angular -ui-bootstrap документация датпикера). Надеюсь, это не имеет никакого значения.
Почему текст выводится на моем входе неправильно и как это делается правильно?
Update
Тем временем я сделал небольшой прогресс. После более подробного ознакомления с подробностями о компиляции и ссылке в this plunkr Я использую функцию компиляции, а не функцию ссылки, чтобы выполнять мои манипуляции с DOM. Я все еще немного смущен этой выдержкой из документов:
Примечание: экземпляр шаблона и экземпляр ссылки могут быть разными объектами, если шаблон был клонирован. По этой причине небезопасно делать что-либо другое, кроме преобразований DOM, которые применяются ко всем клонированным узлам DOM в функции компиляции. В частности, регистрация слушателя DOM должна выполняться в функции связывания, а не в функции компиляции.
Особенно интересно, что подразумевается под ", которые применяются ко всем клонированным узлам DOM". Первоначально я думал, что это означает, что это применимо ко всем клонам шаблона DOM, но это, похоже, не так.
Во всяком случае: Моя новая версия компиляции отлично работает с хромом. В Firefox мне нужно сначала выбрать дату, используя средство выбора даты, и после этого все будет хорошо (проблема с Firefox решена сама собой, если я изменю undefined на null (plunkr) в парсере даты выбора даты). Так что это тоже не последняя вещь. Кроме того, я использую ng-model2
вместо ng-model
, который я переименовываю во время компиляции. Если я этого не сделаю, все еще сломано. По-прежнему не знаю, почему.
Ответы
Ответ 1
Ваша директива будет работать, если вы добавите эти 2 строки в определение своей директивы:
return {
priority: 1,
terminal: true,
...
}
Это связано с порядком выполнения директив.
Итак, в вашем коде
<input my-datepicker="" type="text" ng-model="container.two" id="myDP" />
Существуют две директивы: ngModel
и myDatepicker
. С приоритетом вы можете выполнить свою собственную директиву до выполнения ngModel.
Ответ 2
Честно говоря, я не совсем уверен, почему это вызвало и почему ваша дата была "toString-ed", прежде чем показывать ее на входе.
Однако я нашел места для реструктуризации вашей директивы и удаления ненужного кода, такого как $compile
сервис, изменения атрибутов, наследование областей, require
в директиве и т.д. Я использовал изолированную область, так как я не думайте, что каждое использование директивы должно знать родительскую область, поскольку это может привести к порочным ошибкам в будущем. Это моя измененная директива:
angular.module('ui.bootstrap.demo').directive('myDatepicker', function() {
return {
restrict: 'A',
scope: {
model: "=",
format: "@",
options: "=datepickerOptions",
myid: "@"
},
templateUrl: 'datepicker-template.html',
link: function(scope, element) {
scope.popupOpen = false;
scope.openPopup = function($event) {
$event.preventDefault();
$event.stopPropagation();
scope.popupOpen = true;
};
scope.open = function($event) {
$event.preventDefault();
$event.stopPropagation();
scope.opened = true;
};
}
};
});
И ваше использование HTML будет:
<div my-datepicker model="container.two"
datepicker-options="dateOptions"
format="{{format}}"
myid="myDP">
</div>
Изменить. Добавлен параметр id
в качестве параметра для директивы. Plunker обновлен.
Plunker
Ответ 3
Я думаю, что ответ от @omri-aharon - лучший, но я хотел бы указать на некоторые улучшения, которые здесь не упоминались:
Вы можете использовать конфигурацию для равномерного задания ваших параметров, таких как формат и текстовые параметры, следующим образом:
angular.module('ui.bootstrap.demo', ['ui.bootstrap'])
.config(function (datepickerConfig, datepickerPopupConfig) {
datepickerConfig.formatYear='yy';
datepickerConfig.startingDay = 1;
datepickerConfig.showWeeks = false;
datepickerPopupConfig.datepickerPopup = "shortDate";
datepickerPopupConfig.currentText = "Heute";
datepickerPopupConfig.clearText = "Löschen";
datepickerPopupConfig.closeText = "Schließen";
});
Я считаю, что это проще и проще обновлять. Это также позволяет значительно упростить директиву, шаблон и разметку.
Пользовательская директива
angular.module('ui.bootstrap.demo').directive('myDatepicker', function() {
return {
restrict: 'E',
scope: {
model: "=",
myid: "@"
},
templateUrl: 'datepicker-template.html',
link: function(scope, element) {
scope.popupOpen = false;
scope.openPopup = function($event) {
$event.preventDefault();
$event.stopPropagation();
scope.popupOpen = true;
};
scope.open = function($event) {
$event.preventDefault();
$event.stopPropagation();
scope.opened = true;
};
}
};
});
Шаблон
<div class="row">
<div class="col-md-6">
<p class="input-group">
<input type="text" class="form-control" id="{{myid}}" datepicker-popup ng-model="model" is-open="opened" ng-required="true" />
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
</div>
</div>
Как использовать его
<my-datepicker model="some.model" myid="someid"></my-datepicker>
Кроме того, если вы хотите принудительно использовать форматирование немецкого языка, вы можете добавить angular -locale_de.js. Это обеспечивает единообразие в использовании констант даты, например 'shortDate'
, и заставляет использовать немецкие имена месяца и дня.
Ответ 4
Вот мой патч обезьяны вашего плункера,
http://plnkr.co/edit/9Up2QeHTpPvey6jd4ntJ?p=preview
В основном, я сделал, чтобы изменить вашу модель, которая является датой, вернуть форматированную строку с помощью директивы
.directive('dateFormat', function (dateFilter) {
return {
require:'^ngModel',
restrict:'A',
link:function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) {
viewValue.toString = function() {
return dateFilter(this, attrs.dateFormat);
};
return viewValue;
});
}
};
});
Вам нужно передать атрибут date-format
для вашего тега input
.
Если бы я был вами, я бы не пошел так далеко, чтобы составить сложную директиву. Я просто добавлю <datepicker>
к вашему тегу input
с той же ng-моделью, а также кнопками управления show/hide. Вы можете поэкспериментировать свой вариант, начиная с моего плункера
Ответ 5
Если создание директивы - это удобство добавления атрибутов, вы можете иметь 2 директивы на исходном вводе:
<input my-datepicker="" datepicker-popup="{{ format }}" type="text" ng-model="container.two" id="myDP" />
Затем избегайте множественных областей выделения, изменяя scope: true
на scope: false
в директиве myDatepicker
.
Это работает, и я думаю, что предпочтительнее создать дополнительную директиву для изменения ввода даты в желаемый формат:
http://plnkr.co/edit/23QJ0tjPy4zN16Sa7svB?p=preview
Почему вы добавляете атрибут из директивы, эта проблема не имеет понятия, она почти такая же, как у вас есть два сборщика даты на одном и том же входе, один с вашим форматом и один со значением по умолчанию, который применяется после.
Ответ 6
Используйте moment.js с компонентом datepicker ui-bootstrap, чтобы создать директиву, чтобы предоставить полный набор шаблонов для форматов даты. Вы можете принять любой формат времени в пределах выделенной области.
Ответ 7
Если кто-либо заинтересован в реализации Typescript (на основе кода @jme11):
Директива
'use strict';
export class DatePickerDirective implements angular.IDirective {
restrict = 'E';
scope={
model: "=",
myid: "@"
};
template = require('../../templates/datepicker.tpl.html');
link = function (scope, element) {
scope.altInputFormats = ['M!/d!/yyyy', 'yyyy-M!-d!'];
scope.popupOpen = false;
scope.openPopup = function ($event) {
$event.preventDefault();
$event.stopPropagation();
scope.popupOpen = true;
};
scope.open = function ($event) {
$event.preventDefault();
$event.stopPropagation();
scope.opened = true;
};
};
public static Factory() : angular.IDirectiveFactory {
return () => new DatePickerDirective();
}
}
angular.module('...').directive('datepicker', DatePickerDirective.Factory())
Шаблон:
<p class="input-group">
<input type="text" class="form-control" id="{{myid}}"
uib-datepicker-popup="MM/dd/yyyy" model-view-value="true"
ng-model="model" ng-model-options="{ getterSetter: true, updateOn: 'blur' }"
close-text="Close" alt-input-formats="altInputFormats"
is-open="opened" ng-required="true"/><span class="input-group-btn"><button type="button" class="btn btn-default" ng-click="open($event)"><i
class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
Использование:
<datepicker model="vm.FinishDate" myid="txtFinishDate"></datepicker>
Ответ 8
Я попытался сделать эту работу (несколько взломать), что может быть не совсем то, что вы хотите, только некоторые грубые идеи. Поэтому вам все равно нужно немного подстроить его. Плункер:
`http://plnkr.co/edit/aNiL2wFz4S0WPti3w1VG?p=preview'
В принципе, я изменил область действия директивы, а также добавлю часы для области var container.two.