1. ホーム
  2. javascript

[解決済み】AngularJSでディレクティブからディレクティブを追加する

2022-04-17 21:25:17

質問

を処理するディレクティブを作成しようとしています。 ディレクティブを追加する を宣言している要素に追加します。 例えば、次のようなディレクティブを作りたいのです。 datepicker , datepicker-languageng-required="true" .

もしこれらの属性を追加してから $compile 明らかに無限ループが発生するので、必要な属性がすでに追加されているかどうかをチェックしているのです。

angular.module('app')
  .directive('superDirective', function ($compile, $injector) {
    return {
      restrict: 'A',
      replace: true,
      link: function compile(scope, element, attrs) {
        if (element.attr('datepicker')) { // check
          return;
        }
        element.attr('datepicker', 'someValue');
        element.attr('datepicker-language', 'en');
        // some more
        $compile(element)(scope);
      }
    };
  });

もちろん、もし私が $compile 要素に設定すると、属性は設定されますが、ディレクティブはブートストラップされません。

この方法は正しいのでしょうか、それとも私のやり方が間違っているのでしょうか?同じ動作を実現するための良い方法はありますか?

UDPATE という事実を考えると $compile はこれを実現する唯一の方法ですが、最初のコンパイルパスをスキップする方法はありますか(要素に複数の子が含まれる場合があります)?おそらく terminal:true ?

アップデイト2 : ディレクティブを select 要素で、予想通りコンパイルが2回実行され、つまり、予想される option s.

解決方法は?

一つの DOM 要素に複数のディレクティブがあり、かつ、そのうちの一つが の順番で適用することが重要です。 priority プロパティで を適用します。数字の大きい方が先に実行されます。指定しない場合、デフォルトの優先順位は0です。

EDIT : 議論の結果、これが完全な動作ソリューションです。キーポイントは 属性を削除する : element.removeAttr("common-things"); であり、さらに element.removeAttr("data-common-things"); (を指定した場合)。 data-common-things を指定します。)

angular.module('app')
  .directive('commonThings', function ($compile) {
    return {
      restrict: 'A',
      replace: false, 
      terminal: true, //this setting is important, see explanation below
      priority: 1000, //this setting is important, see explanation below
      compile: function compile(element, attrs) {
        element.attr('tooltip', '{{dt()}}');
        element.attr('tooltip-placement', 'bottom');
        element.removeAttr("common-things"); //remove the attribute to avoid indefinite loop
        element.removeAttr("data-common-things"); //also remove the same attribute with data- prefix in case users specify data-common-things in the html

        return {
          pre: function preLink(scope, iElement, iAttrs, controller) {  },
          post: function postLink(scope, iElement, iAttrs, controller) {  
            $compile(iElement)(scope);
          }
        };
      }
    };
  });

Working plunkerは下記で入手可能です。 http://plnkr.co/edit/Q13bUt?p=preview

または

angular.module('app')
  .directive('commonThings', function ($compile) {
    return {
      restrict: 'A',
      replace: false,
      terminal: true,
      priority: 1000,
      link: function link(scope,element, attrs) {
        element.attr('tooltip', '{{dt()}}');
        element.attr('tooltip-placement', 'bottom');
        element.removeAttr("common-things"); //remove the attribute to avoid indefinite loop
        element.removeAttr("data-common-things"); //also remove the same attribute with data- prefix in case users specify data-common-things in the html

        $compile(element)(scope);
      }
    };
  });

DEMO

を設定しなければならない理由を説明します。 terminal: truepriority: 1000 (が多い)。

DOM の準備ができると、angular は DOM を走査して登録されたすべてのディレクティブを識別し、ディレクティブをひとつずつ priority これらのディレクティブが同じ要素にある場合 . カスタムディレクティブの優先度を高くして、確実にコンパイルされるようにします。 最初 と共に terminal: true となり、他のディレクティブは スキップ このディレクティブがコンパイルされた後に

カスタムディレクティブがコンパイルされると、ディレクティブを追加したり自分自身を削除したりして要素を変更し、$compile サービスを使用して すべてのディレクティブをコンパイルする (スキップされたものも含む) .

を設定しない場合は terminal:truepriority: 1000 の場合、いくつかのディレクティブがコンパイルされる可能性があります。 前に カスタムディレクティブの そして、カスタムディレクティブが $compile を使って要素 => をコンパイルするとき、すでにコンパイルされているディレクティブを再びコンパイルしてしまいます。これは、特にカスタムディレクティブの前にコンパイルされたディレクティブがすでに DOM を変換している場合、予測不可能な動作を引き起こします。

優先順位と端末の詳細については、以下を参照してください。 ディレクティブの `terminal` を理解するには?

テンプレートも修正するディレクティブの例は、次のとおりです。 ng-repeat (優先度 = 1000) の場合 ng-repeat がコンパイルされます。 ng-repeat 他のディレクティブが適用される前に、テンプレート要素のコピーを作成します。 .

Izhaki さんのコメントのおかげで、以下が参考になりました。 ngRepeat のソースコードです。 https://github.com/angular/angular.js/blob/master/src/ng/directive/ngRepeat.js