23

目標: 2 つの兄弟要素 (それぞれ独自のディレクティブ) 間の通信を備えたディレクティブを使用して動作を作成します。

例で使用する動作: 記事のコンテンツはデフォルトで非表示になっています。タイトルをクリックすると、関連記事の内容が表示されるようにしたい。

問題点: 関連する article 要素は、単一の親要素またはディレクティブにネストすることなく、互いに関連付ける必要があります。

<div article="article1">this is my header</div>
<div id="article1" article-content>this is content for the header above</div>

<div article="article2">this is my header</div>
<div id="article2" article-content>this is content for the header above</div>

article ディレクティブ内にコンテンツを配置する方が簡単なのはわかっていますが、この質問は、このような状況を解決する方法を見つけることです。

content ディレクティブ自体を関連する article ディレクティブに渡すことはできますか?

このコードは今のままではあまり役に立ちませんが、出発点です。どうすればこれを達成できますか?

.directive('article', function(){
  return {
    restrict: "A",
    controller: function($scope) {
      $scope.contentElement = null;
      this.setContentElement = function(element) {
        $scope.contentElement = element;
      }
    },
    link: function(scope, element) {
      element.bind('click', function(){
        // Show article-content directives that belong
        // to this instance (article1) of the directive
      }
    }
  }
}
.directive('articleContent', function(){
  return {
    require: "article",
    link: function(scope, element, attrs, articleCtrl) {
      // Maybe reference the article i belong to and assign element to it?
      // I can't though because these are siblings.
    }
  }
}
4

6 に答える 6

32

兄弟ディレクティブを要求できるディレクティブrequireオプションはありません (私の知る限り)。だけ可能です:

  • を使用して、要素にrequirerequire: "directiveName"
  • angular を使用して DOM ツリーを検索するように指示しますrequire: "^directiveName"
  • またはrequire: "^?directiveName"、必ずしも親コントローラーが必要ない場合
  • またはrequire: "^\?directiveName"、必ずしも親 DOM ラッパーが必要ない場合

兄弟間の通信が必要な場合は、通信用の API として機能するディレクティブ コントローラーを使用して、親 DOM 要素にそれらを格納する必要があります。これがどのように実装されるかは、手元のコンテキストに大きく依存します。

これは Angular JSの良い例です(O Reilly)

app.directive('accordion', function() {
  return {
    restrict: 'EA',
    replace: true,
    transclude: true,
    template: '<div class="accordion" ng-transclude></div>',
    controller: function() {

      var expanders = [];

      this.gotOpened = function(selectedExpander) {
        angular.forEach(expanders, function(expander) {
          if(selectedExpander != expander) {
            expander.showMe = false;
          }
        });
      };

      this.addExpander = function(expander) {
        expanders.push(expander);
      }

    }
  }
});

app.directive('expander', function() {
  return {
    restrict: 'EA',
    replace: true,
    transclude: true,
    require: '^?accordion',
    scope: { title:'@' },
    template: '<div class="expander">\n  <div class="title" ng-click="toggle()">{{ title }}</div>\n  <div class="body" ng-show="showMe" \n       ng-animate="{ show: \'animated flipInX\' }"\n ng-transclude></div>\n</div>',
    link: function(scope, element, attrs, accordionController) {
      scope.showMe = false;
      accordionController.addExpander(scope);

      scope.toggle = function toggle() {
        scope.showMe = !scope.showMe;
        accordionController.gotOpened(scope);
      }
    }
  }
})

使用法 (翡翠テンプレート):

accordion
    expander(title="An expander") Woohoo! You can see mme
    expander(title="Hidden") I was hidden!
    expander(title="Stop Work") Seriously, I am going to stop working now.
于 2013-08-15T03:11:04.380 に答える
10

serviceまたは、ディレクティブ通信のためだけに作成することもできます。特別なservicevsの利点の 1 つrequireは、ディレクティブが html 構造内の場所に依存しないことです。

于 2014-05-19T17:42:16.993 に答える
3

上記の解決策は素晴らしいものであり、ディレクティブ間の通信を可能にするために親スコープを使用することを検討する必要があります。ただし、実装がかなり単純な場合は、親を使用せずに 2 つの兄弟スコープ間で通信できる簡単な方法が Angular に組み込まれています: $emit$broadcast、および$on.

たとえば、複雑なサービスを利用する navbar 検索ボックスを備えた非常に単純なアプリ階層があり、ページ上の他のさまざまなディレクティブに結果をブロードキャストするためにそのサービスが必要だとします。これを行う 1 つの方法は次のようになります。

検索サービスで

$rootScope.$emit('mySearchResultsDone', {
  someData: 'myData'
}); 

他のいくつかのディレクティブ/コントローラーで

$rootScope.$on('mySearchResultsDone', function(event, data) {
  vm.results = data;
});

そのコードの単純さにはある種の美しさがあります。ただし、多数の異なる場所でブロードキャストおよびリスニングを行っている場合、発行/オン/ブロードキャスト ロジックは非常に迅速に厄介になる可能性があることに留意することが重要です。Google で簡単に検索すると、それがアンチパターンである場合とそうでない場合について、多くの意見を得ることができます。

これらの投稿で、emit/broadcast/on に関するいくつかの良い洞察:

于 2015-08-11T00:15:27.487 に答える
0

記事とそのコンテンツのリストがある場合、ng-repeat を使用して、指示なしでそれを行うことができます

<div ng-repeat="article in articles">
   <div article="article1" ng-click='showContent=true'>{{article.header}}</div>
   <div id="article1" article-content ng-show='showContent'>{{article.content}}</div>
</div>

そのため、コントローラーで記事モデルを定義する必要があります。ng-repeat によって作成されたローカル スコープを使用しています。

更新:フィードバックに基づいて、それらをリンクする必要があります。試すことができます

<div article="article1" content='article1'>this is my header</div>
<div id="article1" article-content>this is content for the header above</div>

そしてあなたの指示で

使用する

link: function(scope, element,attrs) {
      element.bind('click', function(){
        $('#'+attrs.content).show();
      }
    }

そして最後の方法は、コントローラー間で通信するためのメソッド$rootScope.$broadcastを使用することです。scope.$onただし、このアプローチでは、メッセージの送信元と、メッセージを処理する必要がある受信者を追跡する必要があります。

于 2013-08-15T02:39:23.327 に答える
0

私はまったく同じ問題を抱えていて、それを解決することができました。

1 つのディレクティブを取得して他の兄弟ディレクティブを非表示にするために、親ディレクティブを使用して API として機能させました。1 つの子ディレクティブは、その要素への参照を渡すことによって表示/非表示にしないことを親に伝え、もう 1 つの子ディレクティブは親トグル関数を呼び出します。

http://plnkr.co/edit/ZCNEoh

app.directive("parentapi", function() {
  return {
    restrict: "E",
    scope: {},
    controller: function($scope) {
      $scope.elements = [];

      var on = true;
      this.toggleElements = function() {
        if(on) {
          on = false;
          _.each($scope.elements, function(el) {
            $(el).hide();
          });
        } else {
          on = true;
          _.each($scope.elements, function(el) {
            $(el).show();
          });
        }
      }

      this.addElement = function(el) {
        $scope.elements.push(el);
      }
    }
  }
});

app.directive("kidtoggle", function() {
  return {
    restrict: "A",
    require: "^parentapi",
    link: function(scope, element, attrs, ctrl) {
      element.bind('click', function() {
        ctrl.toggleElements();  
      });
    }
  }
});

app.directive("kidhide", function() {
  return {
    restrict: "A",
    require: "^parentapi",
    link: function(scope, element, attrs, ctrl) {
      ctrl.addElement(element);
    }
  }  
});
于 2014-11-05T22:45:48.173 に答える