コンポーネント間のデータバインド
この概念はやや複雑ですが、わかりやすく例を使用して説明します。parentAttr 属性のある c:parent コンポーネントがあるとします。c:parent には、parentAttr 属性の値に初期設定された childAttr のある c:child コンポーネントが含まれます。parentAttr 属性値を c:parent から c:child コンポーネントに渡します。これは、2 つのコンポーネント間のデータバインド (値バインドとも呼ばれる) になります。
1<!--c:parent-->
2<aura:component>
3 <aura:attribute name="parentAttr" type="String" default="parent attribute"/>
4
5 <!-- Instantiate the child component -->
6 <c:child childAttr="{!v.parentAttr}" />
7</aura:component>{!v.parentAttr} はバインド式です。c:child の childAttr 属性値を変更すると、c:parent の parentAttr 属性が影響を受けます。その逆も同様です。
では、次のマークアップを変更してみましょう。
1<c:child childAttr="{!v.parentAttr}" />変更後
1<c:child childAttr="{#v.parentAttr}" />{#v.parentAttr} は非バインド式です。c:child の childAttr 属性値を変更しても、c:parent の parentAttr 属性は影響を受けません。その逆も同様です。
式の構文形式間の違いについて概要を次に示します。
- {#expression} (非バインド式)
- データの更新は、JavaScript で期待どおりに動作します。String などのプリミティブが値によって渡され、親と子の式でのデータ更新は分離しています。
- Array や Map などのオブジェク���は参照として渡されるため、子のデータへの変更は親に伝搬されます。ただし、親の変更ハンドラには通知されません。子に伝搬される親の変更も同様に動作します。
- {!expression} (バインド式)
- どちらのコンポーネントでのデータの更新も、双方向データバインドによって両方のコンポーネントに反映されます。同様に、変更ハンドラは親と子両方のコンポーネントでトリガされます。
非バインド式
別のコンポーネント c:childExpr を含む c:parentExpr コンポーネントのもう 1 つの例を見てみましょう。
c:childExpr のマークアップは次のようになります。
1<!--c:childExpr-->
2<aura:component>
3 <aura:attribute name="childAttr" type="String" />
4
5 <p>childExpr childAttr: {!v.childAttr}</p>
6 <p><lightning:button label="Update childAttr"
7 onclick="{!c.updateChildAttr}"/></p>
8</aura:component>c:parentExpr のマークアップは次のようになります。
1<!--c:parentExpr-->
2<aura:component>
3 <aura:attribute name="parentAttr" type="String" default="parent attribute"/>
4
5 <!-- Instantiate the child component -->
6 <c:childExpr childAttr="{#v.parentAttr}" />
7
8 <p>parentExpr parentAttr: {!v.parentAttr}</p>
9 <p><lightning:button label="Update parentAttr"
10 onclick="{!c.updateParentAttr}"/></p>
11</aura:component>c:parentExpr コンポーネントは、非バウンド式を使用して c:childExpr コンポーネントの属性を設定します。
1<c:childExpr childAttr="{#v.parentAttr}" />childExpr をインスタンス化するときに、childAttr 属性を c:parentExpr の parentAttr 属性の値に設定します。{#v.parentAttr} 構文が使用されているため、v.parentAttr 式は childAttr 属性の値にバインドされません。
c:exprApp アプリケーションは c:parentExpr を囲むラッパーです。
1<!--c:exprApp-->
2<aura:application >
3 <c:parentExpr />
4</aura:application>開発者コンソールで、c:exprApp のサイドバーにある [Preview (プレビュー)] をクリックして、ブラウザにアプリケーションを表示します。
parentAttr と childAttr の両方が「親属性」に設定されます。これは parentAttr のデフォルト値です。
次に、c:childExpr のクライアント側コントローラを作成して、コンポーネントを動的に更新できるようにします。childExprController.js のソースは次のようになります。
1/* childExprController.js */
2({
3 updateChildAttr: function(cmp) {
4 cmp.set("v.childAttr", "updated child attribute");
5 }
6})[Update childAttr (childAttr を更新)] ボタンを押します。この結果、childAttr が「更新された子属性」に更新されます。非バインド式を使用したため、parentAttr の値は変わりません。
1<c:childExpr childAttr="{#v.parentAttr}" />では、c:parentExpr のクライアント側コントローラを追加してみましょう。parentExprController.js のソースは次のようになります。
1/* parentExprController.js */
2({
3 updateParentAttr: function(cmp) {
4 cmp.set("v.parentAttr", "updated parent attribute");
5 }
6})開発者コンソールで、c:exprApp の [Update Preview (プレビューを更新)] をクリックします。
[Update parentAttr (parentAttr を更新)] ボタンを押します。今回は、parentAttr が「更新された親属性」に設定されますが、非バインド式のため childAttr は変わりません。
バインド式
次に、代わりにバインド式を使うようにコードを更新してみましょう。c:parentExpr の次の行を変更します。
1<c:childExpr childAttr="{#v.parentAttr}" />変更後
1<c:childExpr childAttr="{!v.parentAttr}" />開発者コンソールで、c:exprApp の [Update Preview (プレビューを更新)] をクリックします。
[Update childAttr (childAttr を更新)] ボタンを押します。この結果、childExpr のクライアント側コントローラの v.childAttr しか設定しなくても、childAttr と parentAttr の両方が「更新された子属性」に更新されます。バインド式を使用して childAttr 属性を設定したため、両方の属性が更新されました。
変更ハンドラおよびデータバインド
コンポーネントのいずれかの属性の値が変更されたときに、変更ハンドラを自動的に呼び出す (クライアント側コントローラのアクション) ようにコンポーネントを設定できます。
バインド式を使用する場合、親または子コンポーネントの属性を変更すると、両方のコンポーネントの変更ハンドラがトリガされます。非バインド式を使用する場合は、変更がコンポーネント間で伝播されないため、変更された属性を含むコンポーネントのみで変更ハンドラがトリガされます。
では、前述の例に変更ハンドラを追加して、バインド式と非バインド式によってどのような影響を受けるか見てみましょう。
以下は、c:childExpr の更新されたマークアップです。
1<!--c:childExpr-->
2<aura:component>
3 <aura:attribute name="childAttr" type="String" />
4
5 <aura:handler name="change" value="{!v.childAttr}" action="{!c.onChildAttrChange}"/>
6
7 <p>childExpr childAttr: {!v.childAttr}</p>
8 <p><lightning:button label="Update childAttr"
9 onclick="{!c.updateChildAttr}"/></p>
10</aura:component><aura:handler> タグに、変更ハンドラを表す name="change" が設定されています。value="{!v.childAttr}" は、変更ハンドラに childAttr 属性を追跡するよう指示します。childAttr が変更されると、onChildAttrChange というクライアント側コントローラのアクションが呼び出されます。
以下は、c:childExpr のクライアント側コントローラです。
1/* childExprController.js */
2({
3 updateChildAttr: function(cmp) {
4 cmp.set("v.childAttr", "updated child attribute");
5 },
6
7 onChildAttrChange: function(cmp, evt) {
8 console.log("childAttr has changed");
9 console.log("old value: " + evt.getParam("oldValue"));
10 console.log("current value: " + evt.getParam("value"));
11 }
12})以下は、変更ハンドラが設定された c:parentExpr の更新されたマークアップです。
1<!--c:parentExpr-->
2<aura:component>
3 <aura:attribute name="parentAttr" type="String" default="parent attribute"/>
4
5 <aura:handler name="change" value="{!v.parentAttr}" action="{!c.onParentAttrChange}"/>
6
7 <!-- Instantiate the child component -->
8 <c:childExpr childAttr="{!v.parentAttr}" />
9
10 <p>parentExpr parentAttr: {!v.parentAttr}</p>
11 <p><lightning:button label="Update parentAttr"
12 onclick="{!c.updateParentAttr}"/></p>
13</aura:component>以下は、c:parentExpr のクライアント側コントローラです。
1/* parentExprController.js */
2({
3 updateParentAttr: function(cmp) {
4 cmp.set("v.parentAttr", "updated parent attribute");
5 },
6
7 onParentAttrChange: function(cmp, evt) {
8 console.log("parentAttr has changed");
9 console.log("old value: " + evt.getParam("oldValue"));ui
10 console.log("current value: " + evt.getParam("value"));
11 }
12})開発者コンソールで、c:exprApp の [Update Preview (プレビューを更新)] をクリックします。
ブラウザのコンソールを開きます (Chrome の )。
[Update parentAttr (parentAttr を更新)] ボタンを押します。バインド式を使用しているため、c:parentExpr と c:childExpr の両方の変更ハンドラがトリガされます。
1<c:childExpr childAttr="{!v.parentAttr}" />代わりに非バインド式を使用するように c:parentExpr を変更します。
1<c:childExpr childAttr="{#v.parentAttr}" />開発者コンソールで、c:exprApp の [Update Preview (プレビューを更新)] をクリックします。
[Update childAttr (childAttr を更新)] ボタンを押します。この場合は、非バインド式を使用しているため、c:childExpr の変更ハンドラのみがトリガされます。