Content Insertion
Content Insertionは、コンポーネント自身以外のエレメントをコンポーネント自身の特定の位置に挿入することができる仕組みです。うまく活用すればとても再利用性の高いコンポーネントを提供することができます。例えば、外側の枠としての役割を担うTabやDialogなどを再利用可能なコンポーネントとして提供し、その中に表示するコンテンツを別のエレメントとして受け取って表示することなどができます。Web Componentsが策定している<content>と同じ概念で、AngularJSにおけるtransclusionにも類似した概念です。
まず、template内に<content>タグをもつコンポーネントを定義します。次に、コンテンツとなるエレメントをコンポーネントの内側のノードとして記述すると、実行時にコンテンツとなるエレメントがtemplate内の<content>タグの部分に挿入されます。
<div id="app"> <my-component> <p>This is some original content</p> <p>This is some more original content</p> </my-component> </div> <script> var parent = new Vue({ el: '#app', components: { 'my-component': { template: '<h1>This is my component!</h1><content>このメッセージは何もcontentが挿入されなかった時のみ表示される</content>', } } }); </script>
上記のコードを実行した結果は以下のようになります。
![図4. Content Insertionの実行例](http://cz-cdn.shoeisha.jp/static/images/article/8567/8567_03a.gif)
なお、<content>タグを複数用いて受け渡しされる複数のエレメントを別々に割り振りたい場合は、<content>のselect属性にCSSセレクタを指定することも可能です。詳しくは公式ドキュメントをご参照ください。
コンポーネント間のイベント
Vue.jsでは、コンポーネントを複数組み合わせてアプリケーションを構築するために、コンポーネント間でイベントをやり取りする仕組みが用意されています。親子間でVMインスタンスに直接アクセスすることもできますが、イベント通信の仕組みを利用することで疎結合なコンポーネント間の連携を実現できます。一度親子間の関係が構築されるとVue.jsでは以下の表に示すイベントのやりとりが可能になります。
項目 | 説明 |
---|---|
$dispatch(event, [args...]) | 指定したイベントを自身の親方向へ発信する。$rootまで順番伝播され、callbackがfalseを返した場合はその伝播が中断される。 |
$broadcast(event, [args...]) | 親から子方向へイベントを発信する。末端の子まで伝播し、callbackでfalseを返した場合は伝播がそのインスタンス自身以降は中断される。 |
$emit(event, [args...]) | 自身のインスタンス内のみでイベントを発信する。 |
$on(event, callback) | 特定のイベントをlistenし、そのイベントに対するcallbackを登録する。 |
$once(event, callback) | 特定のイベントに対して、一度だけ呼ばれるcallbackを登録する。 |
$off([event, callback]) | 引数がない場合はlistenしているすべてのイベントを削除する。eventとcallbackが与えられた場合は、指定したeventに対するcallbackを削除する。 |
以下の例では、初期化時に子コンポーネントがイベントをdispatchして、親がそのイベントに対してcallbackを呼び出しています。
<div id="parent"> <div v-component="child"></div> </div> <script> var Child = Vue.extend({ template: '<div>This is a child.</div>', created: function () { this.$dispatch('child-created', this); } }); var parent = new Vue({ el: '#parent', components: { child: Child }, created: function () { this.$on('child-created', function (child) { console.log('new child created: '); console.log(child); }); } }); </script>
コンポーネントにおけるディレクティブのスコープ
コンポーネント使用時のエレメントに対するディレクティブ(v-showやv-onなど)のスコープは親のものになります。
以下の例のコンポーネントに指定しているエレメント上のv-onは親スコープのonClickParentが参照されます。同様に、Content Insertionによってコンポーネントへ挿入されるエレメントで指定するv-showディレクティブも親スコープです。一方、コンポーネント自身のテンプレート上のv-onディレクティブでは、スコープがコンポーネント自身となります。
<div id="app"> <input v-model="parentMsg"> <!-- componentを指定するエレメントとContent Insertion上でのディレクティブは親スコープ --> <div v-component="child" v-with="childMsg: parentMsg" v-on="click: onClickParent"> <span v-show="active">Content Insertionされる要素も親のスコープが適用される</span> </div> </div> <script> var parent = new Vue({ el: '#app', data: { active: true, parentMsg: 'Inherited message' }, methods: { onClickParent: function(){ alert("parent clicked"); } }, components: { child: { // コンポーネント内テンプレートで指定するディレクティブは子自身のスコープ template: '<span v-on="click: onClickChild">{{childMsg}} <content></content></span>', methods: { onClickChild: function(){ alert("child clicked"); } } } } }); </script>
ダイナミックコンポーネント
v-componentの値にMustache記法による変数を指定することで動的にコンポーネントが切り替わる仕組みがダイナミックコンポーネントです。
<div id="app"> <ul> <li><a v-on="click: switchView('home')">Home</a></li> <li><a v-on="click: switchView('posts')">Post</a></li> <li><a v-on="click: switchView('archive')">Archive</a></li> </ul> <div v-component="{{currentView}}" keep-alive> <!-- 展開されるコンポーネントがvm.currentviewの値に応じて変わる --> </div> </div> <script> var parent = new Vue({ el: '#app', data: { currentView: 'home' }, methods: { switchView: function(componentId){ this.currentView = componentId; } }, components: { home: { template: '<p>This is Home.</p>' }, posts: { template: '<p>This is Posts.</p>' }, archive: { template: '<p>This is Archive.</p>' } } }); </script>
上記の例ではダイナミックコンポーネントのkeep-aliveというディレクティブのパラメーターを与えています。keep-aliveがセットされていると、コンポーネントが切り替わって再度表示される際に、以前のコンポーネントの状態が保持され、DOMの再レンダリングを避けることができます。
最後に
本稿では、Vue.jsの再利用性の高いモジュールを提供するための仕組みであるカスタムフィルター、カスタムディレクティブ、コンポーネントについて公式ドキュメントを元に解説しました。
次回以降も引き続きVue.jsに関する記事の続編を執筆する予定です。より本格的な自作コンポーンネントの例や、トランジション、モジュール管理、ルーター、Ajax通信など、本格的にアプリケーション開発をしていく上で必要となる仕組みについて紹介したいと思います。
LINE株式会社ではJavaScriptエンジニアを募集しています。私たちと一緒にプラットフォーム・グローバル展開を発展させていきたい方はぜひご応募ください。