uiブートストラップデートピッカーのangularJsラッパーディレクティブを作成する方法

ui.bootstrap.datepicker ディレクティブを使用して日付フィールドを表示しています。しかし、ほとんどの場合、同じ設定が必要です。ポップアップとポップアップボタンを組み合わせて、テキストにドイツ語の名前を付けたいと思います。それはボタンとテキスト、そしてフォーマットのために同じコードを何度も作成するので、私は自分自身を繰り返さないように私自身のディレクティブを書きました。

Here is a plunkr with my directive. However I seem to be doing it wrong. If you choose a date with the date picker using the "Date 1" datepicker that does not use my directive everything works fine. I'd expect the same for Date 2, but instead of displaying the date according to the template I supplied in the input field (or any other value I expected) it displays the .toString() representation of the date object (e.g. 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">' +
                '' +
                  '<button type="button" class="btn btn-default" ng-click="' + controllerName + '.openPopup($event)"></button>' +
                '' +
              '</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-からplunkerを分岐したため、plunkerは1.2にあります)。ブートストラップ datepickerのドキュメント)。これによって何も変わらないことを願っています。

入力のテキスト出力が間違っているのはなぜですか。また、それはどのように正しく行われますか。

更新

その間に私は少し進歩しました。コンパイルとリンクの詳細については、こちらのplunkr をご覧ください。私のDOM操作をするためのリンク関数。私はまだドキュメントからのこの抜粋によって少し混乱しています:

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

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

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

50
@ jme11:もう一度試しました。しかし、少なくとも私のFirefox(37.0.1)は、このフィールドに一時的に無効にするような入力を拒否しています。
追加された 著者 yankee,
私があなたの最新のPlunkr ng-modelを使っているなら、私のすべてのブラウザ(Safari、Chrome、Firefox)でうまく動作することを言及したいだけです。私が変更したのは、ng-model2をng-modelに置き換え、ng-model2のset-if-not-setビットをコメントアウトすることだけでした。もう一度テストしたいと思うかもしれません。
追加された 著者 jme11,
これは私に絶対に困惑させました! plunerを開いてui-bootstrap-tpls-0.12.1.jsの1541行にブレークポイントを設定してから、カスタムディレクティブdatepickerから日付を一瞬選択すると、その日付はテキストボックスに表示されます。デバッグを停止すると、toStringバージョンによって上書きされます。
追加された 著者 TwitchBronBron,
私はUIピッカーで同じ日付のISO文字列の問題に遭遇しました、日付文字列を日付オブジェクトに変換するためにモデルゲッターセッターを使用することによってこれを回避することができました。
追加された 著者 Chris Gunawardena,

8 答え

正直なところ、入力に表示される前に、なぜそれが原因で、日付が "toString"になったのか、よくわかりません。

ただし、 $ 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: Added the id as a parameter to the directive. Plunker has been updated.

プランカー

17
追加された
その解決策の問題は、私が自分のディレクティブで設定したデフォルト値を上書きする機能を失うことです。さらに、 id フィールドは<input> 要素に設定されていませんが、
追加された 著者 yankee,
@OmriAharon:はい、あなたの解決策はうまくいくでしょう。しかし、まだ解決されていません。存在しないIDを参照していません。さらに、新しいものが登場した場合に必要なすべての属性を含めるようにディレクティブを調整する必要があり、コストパフォーマンス以外に何もしない多数のスコープウォッチを追加します。さらに ng-modelid の使用にはドキュメントは必要ありません。開発者はこれらの属性を知っています。 myidmodel は異なります。それは動作しますが、それはきれいではありません...
追加された 著者 yankee,
@DaveAlperovich:私のディレクティブから、オリジナルでは提供されていないものを入手したいですか? 1.いくつかのデフォルト値、2.ポップアップを開くボタンの自動追加。そうでなければどこにでもコピー&ペーストする必要があり、DRYの原則に違反するコード。
追加された 著者 yankee,
myid 属性としてIDを追加しました。それは問題を引き起こします:もし私が全くidを指定しないならどうなりますか?そうすれば、 myid と空の文字列になりますよね。私がそれらのうちの2つを持っているならば、これはIDのユニークさへの違反です。 myid属性を見て、外の世界によって設定されていない場合はランダムな値に設定することで、これを解決できると思います。そして、オーバーライド可能なデフォルト値を設定したいすべての属性に対してこれを実行する必要があります。もう1つの(それほど重要ではない)問題は、ラベルが存在しないIDを参照している場合、通常私のIDEが警告を出しますが、IDEはmyidについて知らないということです。
追加された 著者 yankee,
@yankee、元のリポジトリをフォークし、元のDirective/Controllerをあなたが望むものに変更します。現在のものをあなたの好きなように振る舞わせることよりも、それほど問題はありません。
追加された 著者 Dave Alperovich,
Omriがしたことは、オリジナルのDatePicker Directiveを自分のDirective/Templateでラップすることですが、今でも仕事の道具はブートストラップ-UI-DatePickerです。利点が何であるかはよくわかりませんが、Omriの弁護の@yankeeでは、彼のオリジナルでは提供されていないことがあなたのディレクティブから何が欲しいのか不明です...
追加された 著者 Dave Alperovich,
@ jme11良いアイデア:)
追加された 著者 Omri Aharon,
@ jme11たった1つだけ、datepicker HTMLディレクティブの宣言は(偶然にも)myidの代わりに id 属性を使用しているため一意性の問題が発生します。@yankee - おそらくラベルを開いて欲しいと思うでしょう。ポップアップと思いますか?そこで、入力の id とボタンの {{id}} _ button に少しシフトしました。 plnkr.co/edit/jevzBrCLeLiSNwe6KAk4?p =プレビュー
追加された 著者 Omri Aharon,
@yankee私の label に対する解決策は、0 $ timeout label HTML要素を作成する dynamicLabel ディレクティブです。 >、それはうまくいくはずです。私は1年以上の純粋なAngularの経験があり、私が最初から作成したプロジェクトについて多くの開発者を指導しました、そして彼らの直感は modelng-modelmyIdid になります。それらを信じてください:-)私は追加の属性については同意します、しかしそれはあなたに柔軟性を持つ強い指令を与えるでしょう、あなたがそれらを必要としないならあなたはそれらすべてを渡す必要はありません。
追加された 著者 Omri Aharon,
@yankee IDを指定する必要はありません。ディレクティブを使用するときにIDを発行できます。起こることは、入力が空のid属性を持つことです、そしてそれはそれです、何の罰もありません。それを削除したい場合は、渡された myid パラメータに値がある場合はlink関数に id 属性を追加し、それ以外の場合は何もせずに入力をそのままにします id
追加された 著者 Omri Aharon,
@yankee Angular 1.4とそのオプションには慣れていないので、あなたが書いた gettSetter オプションについて少し読んでください。私はそれがパラメータを使って同じ方法で、そして/または最悪の場合 - これをラップするディレクティブ - を収容することができると確信しています。
追加された 著者 Omri Aharon,
@ABOS何がバグの原因となったのかを知ることは興味深いことに同意しますが、私の考えではより良いアーキテクチャーであるように、より良いディレクティブ構造を使用できるという事実は変わりません。それでも「そしてそれはどのように正しく行われているのか」に対する答えです。 OPの質問の一部です。
追加された 著者 Omri Aharon,
と言っていますテンプレートに残ったのはハードコーディングされた変数でした。今では修正されています。
追加された 著者 Omri Aharon,
@ jme11ありがとう、それが重要なのです:-)
追加された 著者 Omri Aharon,
@yankeeあなたは更新を見ることができます。重要なのは、あなたの指示はあなたが望むのと同じくらい柔軟にできるということです。あなたはそれを際限なくパラメータ化することができます。
追加された 著者 Omri Aharon,
@yankeeこれらはすべて簡単に解決できる問題です。あなたがする必要があるのは、単に別の属性を追加してそれを独立したスコープで取得することだけです。 id のためにそれをする方法を示すために私はすぐに更新するつもりです。
追加された 著者 Omri Aharon,
@OmriAharon IDを指摘していただきありがとうございます。私は意図的にmyIdではなくidに変更して前述の他の問題のいくつかに対処しましたが、link関数からelement.removeAttr( 'id')を追加するのを忘れていました。私は今それを更新しました。たとえそれがOPには最適ではないように見えても、私はあなたのアプローチが最も明確であると思います(そして最終的には保守性にとって最善となるでしょう)。このサイトの性質を考えると、それは他の人には確かにインスピレーションになるでしょう(それは明らかにあなたが得た投票数に基づいているからです)。
追加された 著者 jme11,
このスレッドに従って、label要素をディレクティブに追加するべきだと私は主張します。これにより、idがラベルのfor属性と一致しない可能性がなくなり、アクセシビリティが保証されます。ラベルを常に表示したくない場合は、私のPlunkr plnkr.co/edit>を更新しました/ NvdwXkPIBvLVzDadjEo0?p =プレビューにして、Boostrapの.sr-onlyクラスを使用して必要に応じてラベルを非表示にする方法を説明します。ただし、ラベルをスクリーンリーダーで読み取り可能にします。さらに、ラベル自体にデフォルト値を追加したので、マークアップに追加する必要はありません。
追加された 著者 jme11,
問題ない。もう1つ重要なのは、OPが既存のディレクティブのラッパーについて質問しているときに、あなたのアプローチはAngular UI DatePickerからの標準的なアプローチであるということです。あなたの略奪者でさえうまくいっても、それはまだOPの最初の質問に答えません。私見、この場合に何が起こったのかの深い理解はもっと面白いでしょう。
追加された 著者 ABOS,
「選択日2は:」というテキストは、何も表示されていません。
追加された 著者 ABOS,
このアップデートはまったく機能しませんでした。
追加された 著者 ABOS,

あなたのディレクティブはあなたがあなたのディレクティブ定義にこれらの2行を追加するときに働くでしょう:

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

これはディレクティブが実行される順番と関係があります。

だからあなたのコードで

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

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

9
追加された
ブリリアント! terminal はまさに私が欠けていたものでした:-)。 (これ以上テストをする必要はありませんが、 plunkrをここにしたことでうまくいきました。) )
追加された 著者 yankee,
terminalプロパティは、Angularに対して、その要素の後に続くすべてのディレクティブをスキップするように指示します。指示文の終端を理解する方法 "> stackoverflow.com/questions/15266840/…
追加された 著者 jediz,

@ 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: "@"
      },
      テンプレートUrl: 'datepicker-テンプレート.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">
        
<input type="text" class="form-control" id="{{myid}}" datepicker-popup ng-model="model" is-open="opened" ng-required="true" /> <button type="button" class="btn btn-default" ng-click="open($event)"></button>

</div> </div>

どうやって使うのですか


さらに、ドイツ語のロケールフォーマットの使用を強制したい場合は、angular-locale_de.jsを追加できます。これは 'shortDate' のような日付定数の使用における統一性を保証し、ドイツの月と日の名前の使用を強います。

4
追加された

これがあなたのプランカーの猿パッチです。

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

If I were you, I would not go that far to make a complex directive. I would simply add a appended to your input tag with the same ng-model, and control show/hide with a button. You may experiment your option starting from my plunker

2
追加された
あなたのplunkrは動きません。日付ピッカーを使用して日付を選択できますが、テキスト入力は使用できません。 Firefoxはdate2フィールドでの入力を拒否し、日付を入力し始めるとすぐにクロムがtoString()日付表現を即座に変換して表示します。
追加された 著者 yankee,
だれ、それがある
追加された 著者 Dave Alperovich,

私はこの作品を作ろうとしました(ややハック)。それであなたはまだそれを少し微調整する必要があります。プランカーは:

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

基本的に、ディレクティブスコープを変更し、スコープvar container.twoの監視も追加しました。

1
追加された
はい、私は自分の変数を監視し、それらを継続的に文字列に変換することができました。しかし、ここで実際に何が起きているのでしょうか。なぜそれが起こっているのですか?
追加された 著者 yankee,

タイプスクリプトの実装に大体興味がある人(@ 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())

テンプレート:


<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"/><button type="button" class="btn btn-default" ng-click="open($event)"></button>

使用法:


0
追加された

日付時刻フォーマットの包括的なパターンセットを提供するディレクティブを作成するには、ui-bootstrap datepickerコンポーネントと共にmoment.jsを使用します。分離範囲内であれば、どのような時刻形式でも受け入れることができます。

0
追加された

ディレクティブを作成して属性を追加するのが便利な場合は、元の入力に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
追加された
Angular - 日本のコミュニティ [ja]
Angular - 日本のコミュニティ [ja]
3 参加者の

このグループではAngularについて話します。 パートナー:kotaeta.com

JavaScript - 日本のコミュニティ
JavaScript - 日本のコミュニティ
2 参加者の

日本人コミュニティのjavascript