[SalesForce] Is it possible to use lightning:recordEditForm dynamically on different object types with variable objectApiName

I need to create an edit component for an SObject from which I do not know the objectApiName at design time. I only know the Id of the SObject. objectApiName could be any valid SObject-name.

Now I want to use lightning:inputField

This needs to be wrapped by lightning:recordEditForm

In case I hardcode it to "Order", provide a vaild Order Id and let the controller load the order and assign it to the Parent-attribute, this works flawlessly:

<aura:attribute name="recordId" type="String" default="" />
<aura:attribute name="Parent" type="SObject" default="{}" />
<aura:attribute name="ParentType" type="String" default="" />
<b>{!v.ParentType}({!v.recordId})</b>
<br/>
<lightning:recordEditForm recordId="{!v.recordId}"  objectApiName="Order">
    <lightning:messages />
    <lightning:inputField fieldName="Name" />
    <lightning:button class="slds-m-top_small" variant="brand" type="submit" name="update" label="Update" />
</lightning:recordEditForm>

It looks like below and even the Update-button works as expected and saves the record:

enter image description here

But what I really want is

<lightning:recordEditForm recordId="{!v.recordId}"  objectApiName="{!v.ParentType}">

Note the {!v.ParentType} instead of the hardcoded "Order"

As an great unfortunate, this only collapses to that error-popup

enter image description here

In the screenshot you see, that the ParentType is correctly bound to v.ParentType (right above the button) which a I also verify in the console

Suspicion

I set the ParentType-attribute as a result of APEX call (asynchronously). This seems to be too late for the lightning:recordEditForm. It tries to render with objectApiName at a time where it is still blank, hence it crashes during initialization…

Question

Is there a clean way to avoid this and get something to fly with an dynamic objectApiName?

Trying to use $A.createComponents()

As @sfdcfox suggested I jump on the $A.createComponents() which turned indeed out to be tricky, exactly as sfdcfox has predicted. Im following this documentation here.

Added a button to the markup

Added a handler to the js-controller:

makeEdit : function(cmp, evt, hlp) { 
    console.log('ParentId :: ', cmp.get("v.ParentId") ); 
    console.log('ParentType :: ', cmp.get("v.ParentType") ); 
    $A.createComponents([
            ["lightning:recordEditForm",{
                "recordId"          : cmp.get("v.ParentId"),
                "objectApiName"     : cmp.get("v.ParentType"),
            }],
            ["lightning:inputField",{
                "fieldName" : "Name"
            }]
        ]
        ,function(components, status, errorMessage){
            if (status === "SUCCESS") {
                var wrapper         = components[0];
                var inner           = components[1];
                wrapper.set("v.body", inner);
            }
            else if (status === "INCOMPLETE") {
                console.log("No response from server or client is offline.")
                // Show offline error
            }
            else if (status === "ERROR") {
                console.log("Error: " + errorMessage);
                // Show error message
            }
        }
    );
},

Now I can press the button – but nothing – I repeat: n o t h i n g – gets rendered at all. What is my fault here? Note that the console.log get output and the attribute are holding good values.

Best Answer

So I faced a similar issue, I managed to solve it using aura:if

aura:if doesnt create the inner components unless the condition is true. You can use it to your advantage.

<aura:if isTrue="{!!empty(v.ParentType)}">
<lightning:recordEditForm recordId="{!v.recordId}"  objectApiName="{!v.ParentType}">
</aura:if>

The inner component and its contents are only created and rendered if the value of the isTrue expression evaluates to true. If the value of the isTrue expression changes and evaluates to false, all the components inside the tag are destroyed. The components are created again if the isTrue expression changes again and evaluates to true.

The general guideline is to use because it helps your components load faster initially by deferring the creation and rendering of the enclosed element tree until the condition is fulfilled.

Thus when your component is loaded even before calling init event, lightning:recordEditForm wont render as v.ParentType will be null.

After your init is called and you set the value of v.ParentType then automatically it will start rendering the lightning:recordEditForm. As ParentType is not null, it wont throw an ugly error.

SRC: https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/components_conditional_markup.htm