如何为ui-bootstrap datepicker创build一个angularJs包装指令?

我正在使用ui.bootstrap.datepicker指令来显示一些date字段。 然而,大多数时候我需要相同的设置:我希望它带有一个popup式窗口和一个popup式button,同时我也想要德文名称作为文本。 这确实为button和文本和格式一遍又一遍地创build相同的代码,所以我写了自己的指令,以防止自己重复自己。

我的指令是一个plunkr 。 但是,我似乎做错了。 如果你selectdateselect器使用“date1”datepicker不使用我的指令一切工作正常。 我期望date2相同,但不是根据我在input字段(或任何其他值我预期)提供的模板显示date,而是显示date对象的.toString()表示forms(例如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" /> 

(概念是从这个答案启发)

我使用的是angular1.3(1.2版的蹦跳者,因为我只是从angular-ui-bootstrap datepicker文档中分叉)。 我希望这没有任何区别。

为什么在我的input文本输出错误,它是如何正确地完成?

更新

在此期间,我取得了一些进展。 在阅读了关于编译和链接细节的更多信息之后,在这个plunkr中,我使用了编译函数而不是链接函数来执行DOM操作。 我从文档摘录中还是有些困惑:

注意:如果模板已被克隆,模板实例和链接实例可能是不同的对象。 由于这个原因,除了适用于编译函数中所有克隆的DOM节点的DOM转换外,其他任何操作都是不安全的。 具体来说,DOM监听器注册应该在链接函数中而不是在编译函数中完成。

特别是我想知道“适用于所有克隆的DOM节点”是什么意思。 我原本以为这意味着“适用于DOM模板的所有克隆”,但似乎并非如此。

总之:我的新编译版本在铬中工作正常。 在Firefox中,我需要先使用dateselect器select一个date,之后,一切工作正常(Firefox的问题解决了自己,如果我更改dateselect器的date分析器中的undefined为空( PLUNK ))。 所以这也不是最新的事情。 此外,我使用ng-model2而不是在编译期间重命名的ng-model 。 如果我不这样做,所有事情都还没有解决。 仍然不知道为什么。

将这2行添加到您的指令定义时,您的指令将起作用:

 return { priority: 1, terminal: true, ... } 

这与指令的执行顺序有关。

所以在你的代码中

 <input my-datepicker="" type="text" ng-model="container.two" id="myDP" /> 

有两个指令: ngModelmyDatepicker 。 有了优先级,你可以让你自己的指令在ngModel之前执行。

说实话,我不太清楚为什么它是由什么引起的,什么原因导致你的date在input之前显示出来“toString-ed”。

但是,我确实find了重构你的指令的地方,并删除了许多不必要的代码,比如$compile服务,属性更改,作用域inheritance,指令中的要求等。我使用了独立的作用域,因为我不认为每个指令使用情况应该知道父范围,因为这可能会导致恶性错误。 这是我改变的指示:

 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

我认为@ omri-aharon的答案是最好的,但是我想指出一些在这里没有提到的改进:

更新了Plunkr

您可以使用configuration来统一设置您的选项,如格式和文本选项,如下所示:

 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。 这确保了使用date常量(如'shortDate'统一性,并强制使用德语月份和date名称。

这里是你的抢劫者的猴子补丁,

http://plnkr.co/edit/9Up2QeHTpPvey6jd4ntJ?p=preview

基本上我所做的就是改变你的模型,这是一个date,使用指令返回格式化的string

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

您需要为您的input标记传递date-format属性。

如果我是你,我不会做出这么复杂的指令。 我会简单地添加一个<datepicker>附加到您的input标签与相同的ng模型,并控制显示/隐藏与一个button。 你可以试试从你的select开始

如果创build该指令是方便添加属性,则可以在原始input上使用2个指令:

 <input my-datepicker="" datepicker-popup="{{ format }}" type="text" ng-model="container.two" id="myDP" /> 

然后通过在myDatepicker指令中更改作用域来避免多个隔离作用域scope: truescope: false

这工作,我认为最好创build一个更进一步的指令,将dateinput更改为所需的格式:

http://plnkr.co/edit/23QJ0tjPy4zN16Sa7svB?p=preview

为什么从指令中添加属性导致这个问题,我不知道,这几乎就像你有两个dateselect器在同一个input,一个与你的格式和一个默认,得到的应用后。

在ui-bootstrap datepicker组件中使用moment.js来创build指令,为date时间格式提供一组全面的模式。 您可以接受隔离范围内的任何时间格式。

如果有人对Typecript实现感兴趣(松散地基于@ 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> 

我试图做这个工作(有些黑客),这可能不是你想要的,只是一些粗略的想法。 所以你仍然需要稍微调整一下。 重击者是:

 `http://plnkr.co/edit/aNiL2wFz4S0WPti3w1VG?p=preview' 

基本上,我改变了指令范围,并添加了范围var container.two。