バブルまたはキャプチャのコンポーネントイベントの処理
このフレームワークでは、キャプチャフェーズとバブルフェーズがコンポーネントイベントの伝達でサポートされます。これらのフェーズは DOM の処理パターンと似ており、対象のコンポーネントがイベントに対応したり、場合によっては後続のハンドラの動作を制御したりできます。キャプチャフェーズはバブルフェーズの前に実行されます。
デフォルトのイベント伝達ルール
デフォルトでは、コンテインメント階層のすべての親がキャプチャフェーズおよびバブルフェーズでイベントを処理できるわけではありません。代わりに、イベントはコンテインメント階層のすべての所有者に伝達されます。
コンポーネントの所有者は、その作成を行うコンポーネントです。宣言的に作成されたコンポーネントの場合、所有者はイベントの起動コンポーネントを参照するマークアップが含まれる、最も外側のコンポーネントになります。プログラムで作成されたコンポーネントの場合、所有者コンポーネントはそのコンポーネントを作成するために $A.createComponent を呼び出したコンポーネントになります。
イベント伝達の方向 (下) はバブルフェーズ (上) と反対になりますが、キャプチャフェーズにも同じルールが適用されます。
わかりやすく、バブルフェーズの例を使用して説明します。
c:owner には c:container が含まれ、さらにそこには c:eventSource が含まれます。
1<!--c:owner-->
2<aura:component>
3 <c:container>
4 <c:eventSource />
5 </c:container>
6</aura:component>c:eventSource がイベントを起動すると、このコンポーネント自体がイベントを処理します。次に、イベントはコンテインメント階層をバブルアップします。
c:container には c:eventSource が含まれますが、マークアップの最も外側のコンポーネントではないことが原因で所有者にはならないため、バブルイベントを処理できません。
c:owner は、c:container がそのマークアップ内にあるため、所有者です。c:owner はイベントを処理できます。
すべてのコンテナコンポーネントへの伝達
デフォルトの動作では、コンテインメント階層内のすべての親がイベントを処理できるわけではありません。他のコンポーネントが含まれているが、それらのコンポーネントの所有者ではないコンポーネントがあります。これらのコンポーネントは、コンテナコンポーネントと呼ばれます。この例の c:container は、c:eventSource の所有者でないため、コンテナコンポーネントになります。デフォルトでは、c:container は c:eventSource が起動したイベントを処理できません。
コンテナコンポーネントには、Aura.Component[] 型のファセット属性 (デフォルトの body 属性など) があります。コンテナコンポーネントには、定義で {!v.body} などの式が使用されているコンポーネントが含まれます。コンテナコンポーネントは、その式で表されるコンポーネントの所有者ではありません。
コンテナコンポーネントでイベントを処理できるようにするには、コンテナコンポーネントの <aura:handler> タグに includeFacets="true" を追加します。たとえば、includeFacets="true" をコンテナコンポーネント c:container のハンドラに追加すると、c:eventSource からバブルされたコンポーネントイベントを処理できるようになります。
1<aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}"
2 includeFacets="true" />バブルイベントを処理する
コンポーネントイベントを起動したコンポーネントは、<aura:registerEvent> タグを使用してイベントを起動したことを登録します。
1<aura:component>
2 <aura:registerEvent name="compEvent" type="c:compEvent" />
3</aura:component>バブルフェーズのイベントを処理するコンポーネントは、<aura:handler> タグを使用してそのクライアント側コントローラの処理アクションを割り当てます。
1<aura:component>
2 <aura:handler name="compEvent" event="c:compEvent" action="{!c.handleBubbling}"/>
3</aura:component>キャプチャイベントを処理する
キャプチャフェーズのイベントを処理するコンポーネントは、<aura:handler> タグを使用してそのクライアント側コントローラの処理アクションを割り当てます。
1<aura:component>
2 <aura:handler name="compEvent" event="c:compEvent" action="{!c.handleCapture}"
3 phase="capture" />
4</aura:component>phase 属性を設定しない場合、コンポーネントイベントのデフォルト処理フェーズはバブルです。
非同期コード実行のイベント伝達を一時停止する
event.pause() を使用して、event.resume() がコールされるまでイベントの処理と伝達を一時停止します。このフロー制御メカニズムは、非同期コードの実行からの応答に基づいて決定を行う場合に便利です。たとえば、ネイティブモバイルコードに対する非同期コールからの応答に基づいてイベント伝達に関する決定を行うことができます。
pause() や resume() は、キャプチャフェーズまたはバブルフェーズでコールできます。
イベントバブルの例
自分でいろいろと試せるように 1 つの例を見てみましょう。
1<!--c:eventBubblingParent-->
2<aura:component>
3 <c:eventBubblingChild>
4 <c:eventBubblingGrandchild />
5 </c:eventBubblingChild>
6</aura:component>最初に、単純なコンポーネントイベントを定義します。
1<!--c:compEvent-->
2<aura:event type="COMPONENT">
3 <!--simple event with no attributes-->
4</aura:event>c:eventBubblingEmitter は、c:compEvent を起動するコンポーネントです。
1<!--c:eventBubblingEmitter-->
2<aura:component>
3 <aura:registerEvent name="bubblingEvent" type="c:compEvent" />
4 <lightning:button onclick="{!c.fireEvent}" label="Start Bubbling"/>
5</aura:component>c:eventBubblingEmitter のコントローラは次のようになります。ボタンをクリックすると、マークアップに登録された bubblingEvent イベントが起動します。
1/*eventBubblingEmitterController.js*/
2{
3 fireEvent : function(cmp) {
4 var cmpEvent = cmp.getEvent("bubblingEvent");
5 cmpEvent.fire();
6 }
7}c:eventBubblingGrandchild には c:eventBubblingEmitter が含まれ、<aura:handler> を使用してイベントのハンドラを割り当てます。
1<!--c:eventBubblingGrandchild-->
2<aura:component>
3 <aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}"/>
4
5 <div class="grandchild">
6 <c:eventBubblingEmitter />
7 </div>
8</aura:component>c:eventBubblingGrandchild のコントローラは次のようになります。
1/*eventBubblingGrandchildController.js*/
2{
3 handleBubbling : function(component, event) {
4 console.log("Grandchild handler for " + event.getName());
5 }
6}ハンドラがコールされると、コントローラはイベント名をログに記録します。
c:eventBubblingChild のマークアップは次のようになります。c:eventBubblingGrandchild は、この例で後から c:eventBubblingParent を作成するときに c:eventBubblingChild のボディとして渡します。
1<!--c:eventBubblingChild-->
2<aura:component>
3 <aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}"/>
4
5 <div class="child">
6 {!v.body}
7 </div>
8</aura:component>c:eventBubblingChild のコントローラは次のようになります。
1/*eventBubblingChildController.js*/
2{
3 handleBubbling : function(component, event) {
4 console.log("Child handler for " + event.getName());
5 }
6}c:eventBubblingParent には c:eventBubblingChild が含まれ、さらにそこには c:eventBubblingGrandchild が含まれます。
1<!--c:eventBubblingParent-->
2<aura:component>
3 <aura:handler name="bubblingEvent" event="c:compEvent" action="{!c.handleBubbling}"/>
4
5 <div class="parent">
6 <c:eventBubblingChild>
7 <c:eventBubblingGrandchild />
8 </c:eventBubblingChild>
9 </div>
10</aura:component>c:eventBubblingParent のコントローラは次のようになります。
1/*eventBubblingParentController.js*/
2{
3 handleBubbling : function(component, event) {
4 console.log("Parent handler for " + event.getName());
5 }
6}次に、コードを実行したらどうなるか確認しましょう。
- ブラウザで、c:eventBubblingParent に移動します。<c:eventBubblingParent /> が含まれる .app リソースを作成します。
- c:eventBubblingEmitter のマークアップに含まれる [バブルを開始] ボタンをクリックします。
- ブラウザのコンソールで出力を確認します。
1Grandchild handler for bubblingEvent 2Parent handler for bubblingEvent
c:compEvent イベントは、c:eventBubblingGrandchild と c:eventBubblingParent (コンテインメント階層で所有者) に対してバブルになります。イベントが c:eventBubblingChild によって処理されることはありません。c:eventBubblingChild は c:eventBubblingParent のマークアップに含まれますが、そのマークアップの最も外側にあるコンポーネントではないために所有者にならないからです。
では、イベント伝達を停止する方法について説明します。伝達を停止するには、c:eventBubblingGrandchild のコントローラを編集します。
1/*eventBubblingGrandchildController.js*/
2{
3 handleBubbling : function(component, event) {
4 console.log("Grandchild handler for " + event.getName());
5 event.stopPropagation();
6 }
7}次に、c:eventBubblingParent に移動して、[バブルを開始] ボタンをクリックします。
ブラウザのコンソールで出力を確認します。
1Grandchild handler for bubblingEventイベントは c:eventBubblingParent コンポーネントにバブルアップされなくなりました。