Lightningコンポーネントには $A.createComponent
や $A.createComponents
メソッドを利用して、コントローラ(or ヘルパー)側で動的にコンポーネントを作成する手段が用意されています。
developer.salesforce.com
2つのメソッドの違いは単一のコンポーネントを作るか、複数の(特にネストした)コンポーネントを作るか、にあります。
$A.createComponents
メソッドを使って、どれくらい複雑なコンポーネントが作れるものなのか確認してみました。
試したサンプルは、lightning:layoutにあるものです(↓)。
<auracomponent>
<div class="c-container">
<lightninglayout horizontalAlign="space">
<lightninglayoutItem flexibility="auto" padding="around-small">
1
</lightninglayoutItem>
<lightninglayoutItem flexibility="auto" padding="around-small">
2
</lightninglayoutItem>
<lightninglayoutItem flexibility="auto" padding="around-small">
3
</lightninglayoutItem>
<lightninglayoutItem flexibility="auto" padding="around-small">
4
</lightninglayoutItem>
</lightninglayout>
</div>
</auracomponent>
そこまで複雑というわけではないですね。。
ただ、公式の例( ui:message
と ui:outputText
)と比べ、複数の子要素( lightning:layoutItem
)を含んでいたりするので、これくらいのものができれば、後は応用が効きそうだということで選定しました。
コンポーネント : DynamicNestedComponents.cmp
Lightningコンポーネントはこんな感じになりました。
コントローラ(後述)で作成した lightning:layout
, lightning:layoutItem
を {! v.contents}
のところで出力させるようにします。
<auracomponent implements="flexipage:availableForAllPageTypes" access="global">
<auraattribute name="contents" type="String" />
<aurahandler name="init" value="{!this}" action="{!c.doInit}"/>
<div class="c-container">
{! v.contents }
</div>
</auracomponent>
なお、 implements="flexipage:availableForAllPageTypes" access="global"
の部分に関しては、動作確認を適当なLightningページでするためのものなので、今回の本筋ではないです。
コントローラ : DynamicNestedComponentsController.js
コンポーネントを作成する肝心のコントローラの doInit
は次のようになりました。
({
doInit : function(component, event, helper) {
$A.createComponents([
["lightning:layout", {
"horizontalAlign" : "space"
}],
["lightning:layoutItem", {
"flexibility" : "auto",
"padding" : "around-small"
}],
["ui:outputText",{
"value" : "1"
}],
["lightning:layoutItem", {
"flexibility" : "auto",
"padding" : "around-small"
}],
["ui:outputText",{
"value" : "2"
}],
["lightning:layoutItem", {
"flexibility" : "auto",
"padding" : "around-small"
}],
["ui:outputText",{
"value" : "3"
}],
["lightning:layoutItem", {
"flexibility" : "auto",
"padding" : "around-small"
}],
["ui:outputText",{
"value" : "4"
}],
],
function(components, status, errorMessage){
if (status === "SUCCESS") {
var layout = components[0];
var layoutItems = [
components[1],
components[3],
components[5],
components[7] ];
var outputTexts = [
components[2],
components[4],
components[6],
components[8] ];
for (var i=0; i<4; i++) {
layoutItems[i].set("v.body", outputTexts[i]);
}
var layoutBody = layout.get("v.body");
for (var i =0; i<4; i++) {
layoutBody.push(layoutItems[i]);
}
layout.set("v.body", layoutBody);
component.set("v.contents", layout);
}
else if (status === "INCOMPLETE") {
console.log("No response from server or client is offline.")
}
else if (status === "ERROR") {
console.log("Error: " + errorMessage);
}
}
);
}
})
長いですね…。
子要素が4つあるサンプルのため冗長であることをを抜きにしても、結構長いです。
注意する点として、 set("v.body", ...)
で中身をセットしたい場合、セットする値もコンポーネントである必要があります。
なので、lightning:layoutItem
の中身は表示したい文字列ではなく、 ui:outputText
コンポーネントになっています *1 。
もし、
layoutItems[i].set("v.body", "" + i);
のようにしてしまうと、
Uncaught Assertion Failed!: Descriptor for Config required for registration : undefined
といったエラーが起きます。
というわけで、複数の子要素を含んだLightningコンポーネントでも一応は動的に作成可能であることが確認できました。