如何为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" />
有两个指令: ngModel
和myDatepicker
。 有了优先级,你可以让你自己的指令在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: true
为scope: 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。
- 使用JavaScript在AngularJS Bootstrap UI中调用模态窗口
- 点击外部时,隐藏Angular UI Bootstrappopup窗口
- $ watch不会触发数据更改
- 结合AngularJS和Twitter Bootstrap的最佳方式
- 支持路由的AngularJS UI Bootstrap选项卡
- 无法在angularjs中调用Object.keys
- 我应该使用Angular UI Bootstrap还是纯Bootstrap 3?
- AngularJS / Angular-ui-bootstrap更改datePicker使用的语言
- 如何使用HTML内容创buildAngularJS UI引导popup窗口?