繁体   English   中英

如何为ui-bootstrap datepicker创建angularJs包装器指令?

[英]How to create an angularJs wrapper directive for a ui-bootstrap datepicker?

我使用ui.bootstrap.datepicker指令来显示一些日期字段。 然而,大多数时候我需要相同的设置:我希望它带有一个弹出按钮和一个弹出按钮,我也想要文本的德语名称。 这确实为按钮和文本以及格式反复创建了相同的代码,因此我编写了自己的指令以防止自己重复自己。

这是我的指令的一个plunkr 但是我似乎做错了。 如果您使用不使用我的指令的“日期1”日期选择器选择日期选择器的日期一切正常。 我期望日期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" />

(概念灵感来自这个答案

我正在使用角度1.3(因为我只是从angular-ui-bootstrap datepicker文档中分叉了plunker,所以它是1.2。 我希望这没有任何区别。

为什么输入中的文本输出错误,如何正确完成?

更新

与此同时,我取得了一些进展。 在阅读了有关编译和链接的详细信息之后,在这个plunkr中我使用编译函数而不是链接函数来执行我的DOM操作。 我对这些文档的摘录感到有些困惑:

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

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

无论如何:我的新编译版本在chrome中运行良好。 在Firefox中我需要首先使用日期选择器选择一个日期,之后一切正常(如果我在日期选择器的日期解析器中将 undefined更改为null( plunkr ),Firefox的问题就解决了)。 所以这也不是最新的事情。 另外我使用ng-model2而不是ng-model ,我在编译时重命名。 如果我不这样做,一切都还是破了。 仍然不知道为什么。

说实话,我不太清楚为什么它会导致你的日期在输入中显示之前是什么导致你的日期“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

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

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

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

所以在你的代码中

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

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

我认为来自@ omri-aharon的答案是最好的,但我想指出一些未在此处提及的改进:

更新了Plunkr

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

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'统一性,并强制使用德语月份和日期名称。

这是我的你的猴子补丁,

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

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

如果我是你,那么我就不会做那么复杂的指令。 我只需添加一个<datepicker>附加到您的input标签与相同的ng模型,并控制显示/隐藏一个按钮。 您可以从我的plunker开始尝试您的选项

如果创建指令是为了方便添加属性,则可以在原始输入上使用2个指令:

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

然后通过更改scope: true避免多个隔离scope: truescope: false myDatepicker指令中的scope: false

这有效,我认为最好创建一个进一步的指令来将日期输入更改为所需的格式:

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

为什么你在指令中添加属性导致这个问题我不知道,这几乎就像你在同一个输入上有2个日期选择器,一个是你的格式,另一个是默认的,后来应用了。

将moment.js与ui-bootstrap datepicker组件一起使用以创建指令,以便为日期时间格式提供全面的模式集。 您可以在隔离范围内接受任何时间格式。

如果有人对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>

我试图让这项工作(有些黑客),这可能不是你想要的,只是一些粗略的想法。 所以你仍然需要调整一下。 吸管是:

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

基本上,我更改了指令范围,还添加了范围var container.two的监视。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM