簡體   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