web-dev-qa-db-ja.com

ui-bootstrap datepickerのangularJsラッパーディレクティブを作成する方法は?

i.bootstrap.datepicker ディレクティブを使用して、日付フィールドを表示しています。ただし、ほとんどの場合、同じ設定が必要です。ポップアップとポップアップボタンが付属し、テキストのドイツ語名も必要です。それはボタンとテキストとフォーマットに対して同じコードを何度も作成するので、私は自分自身が繰り返されるのを防ぐために独自のディレクティブを作成しました。

ここにplunkrがあります 私のディレクティブです。しかし、私はそれを間違っているようです。私のディレクティブを使用しない「Date 1」日付ピッカーを使用して日付ピッカーで日付を選択すると、すべて正常に動作します。 Date 2でも同じことを期待しますが、入力フィールドに入力したテンプレート(または予想されるその他の値)に従って日付を表示する代わりに、dateオブジェクトの.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( angular-ui-bootstrap datepickerドキュメンテーションからプランカーを分岐させただけなので、プランカーは1.2にあります。)違い。

入力のテキスト出力が間違っているのはなぜですか?

更新

その間、私は少し進歩しました。 this plunkr でコンパイルとリンクの詳細を読んだ後、リンク関数ではなくコンパイル関数を使用してDOM操作を行います。私はまだドキュメントからのこの抜粋に少し混乱しています:

注:テンプレートが複製されている場合、テンプレートインスタンスとリンクインスタンスは異なるオブジェクトになる場合があります。このため、コンパイル関数内でクローンされたすべてのDOMノードに適用されるDOM変換以外のことを行うことは安全ではありません。特に、DOMリスナーの登録は、コンパイル関数ではなくリンク関数で行う必要があります。

特に、「クローンされたすべてのDOMノードに適用される」とはどういう意味なのでしょうか。私はもともとこれは「DOMテンプレートのすべてのクローンに適用される」ことを意味すると思っていましたが、そうではないようです。

とにかく:私の新しいコンパイルバージョンは、クロムで正常に動作します。 Firefoxでは、最初に日付ピッカーを使用して日付を選択する必要があり、その後すべてが正常に機能します(日付ピッカーの日付パーサーでundefinedをnullに変更すると( plunkr )Firefoxの問題は解決しました) )。したがって、これも最新のものではありません。さらに、ng-model2 の代わりに ng-modelコンパイル中に名前を変更します。これを行わないと、すべてが壊れます。理由はまだわかりません。

50
yankee

次の2行をディレクティブ定義に追加すると、ディレクティブが機能します。

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

これは、ディレクティブが実行される順序に関係しています。

あなたのコードで

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

ngModelmyDatepickerの2つのディレクティブがあります。 ngModelが実行される前に、優先的に独自のディレクティブを実行できます。

9
Sander_P

正直に言うと、入力に表示する前に、日付が "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>

Edit:ディレクティブのパラメーターとしてidを追加しました。 Plunkerが更新されました。

Plunker

17
Omri Aharon

@ omri-aharonからの答えは最高だと思いますが、ここで言及されていないいくつかの改善点を指摘したいと思います。

更新されたPlunkr

Configを使用して、フォーマットやテキストオプションなどのオプションを次のように均一に設定できます。

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
jme11

ここにあなたのプランカーの私の猿のパッチがあります、

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属性を渡す必要があります。

もし私があなただったら、私はそこまで複雑な指令を作成するつもりはありません。同じngモデルでinputタグに<datepicker>を追加し、ボタンで表示/非表示を制御します。 my plunker からオプションを試すことができます

2
allenhwkim

ディレクティブの作成が属性を追加するのに便利な場合、元の入力に2つのディレクティブを含めることができます。

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

次に、myDatepickerディレクティブでscope: truescope: falseに変更して、複数の分離スコープを避けます。

これは機能し、日付入力を目的の形式に変更するためのディレクティブをさらに作成することをお勧めします。

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

ディレクティブ内から属性を追加するとこの問題が発生する理由はわかりません。同じ入力に2つの日付ピッカーがあり、1つはフォーマットで、もう1つは後に適用されるデフォルトです。

0
gonkan

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>
0
Adam Plocher

Moment.jsとui-bootstrap datepickerコンポーネントを使用してディレクティブを作成し、日時形式の包括的なパターンセットを提供します。分離されたスコープ内の任意の時刻形式を受け入れることができます。

0
Aditya Singh