AngularJS1.5を使っていて、transcludeで中に要素を埋め込むようなコンポーネントで、その埋め込まれる要素内からコンポーネントが提供する機能にアクセスする、ということをしたかった。例えばダイアログ的な枠組みを提供するコンポーネントでtranscludeで内容を埋め込めて、その中のボタンからダイアログを閉じる、みたいな。
しかしtranscludeされる要素は枠組みのコンポーネントとは別の孤立したスコープになってしまい、コンポーネントにはアクセスできないようだった。アクセスさせるにはディレクティブのlinkでtranscludeの制御をする必要があるようだ。
利用側
// Application |
my-dialogがダイアログを提供するディレクティブだとして、その中にレイアウトを記述したものが埋め込まれる、という想定transcludeされる要素内からダイアログが提供するcloseDialog()という関数を呼び出して、ディレクティブ側に通知するtransclude外からはその機能は呼べない
ディレクティブ側
// Directive using transclude |
angularのcomponentだとスコープやコントローラにアクセスする方法がわからなかったので、directiveとして作成transclude: trueでトランスクルードを有効にするtemplate内でng-transcludeを使って埋め込むと、孤立したスコープでtransclude要素が埋め込まれてしまってアクセスする方法がないため、link関数の第5引数のtranscludeFnを使用してtranscludeする要素をクローンして自前で目的の場所に埋め込む- その際のスコープはディレクティブのスコープの親から
$newして、ディレクティブとは別のスコープにしておく - その新しいスコープに提供したい関数(
closeDialog)を定義してやることでtransclude内から参照できるようになる
- その際のスコープはディレクティブのスコープの親から
デモ
http://plnkr.co/edit/ADbo0K?p=preview
その他
- スコープは廃止される、ということなのであまりよくない方法かも…
- 試してないけどAngular2の場合には外側のコンポーネントから
@ViewChildで内側のコンポーネントのコントローラが取得できるので、それ経由でアクセスすればそさそう- AngularJS1.5でも同じようにできなくはないが、親から子コンポーネントへのアクセスが面倒なのがネック
リンク
- AngularJS: Developer Guide: Directives#Creating a Directive that Wraps Other Elements
scope: {}でディレクティブ独自のスコープを作らない場合、外側のスコープを書き換えることになるので一応transclude内からアクセスできるようにはなるが、スコープが汚染されてしまう
- AngularJS: API: $compile:
linkの説明あり - Transclusion and scopes - Angular Tips