8

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.

  • Unfortunately, I think this is one of those times you'd have to do some $A.createComponents logic, which might also get a bit tricky. – sfdcfox May 2 '18 at 16:02
  • just too sad that presumably simple things sometimes turns out complicated in lightning... an interesting observation though: when I add a button with a handler which sets a different recordId on the lightning:recordEditForm, it ignores it (why?) but if I update it to null, it falls into create-new-object-mode - but it's a one-shot: once in new-mode, forever in new-mode and subsequential updates of recordId get ignored again. Looks like lightning:recordEditForm is not designed to get it's properties changed after it's initial rendering. But why? – Uwe Heim May 2 '18 at 16:18
  • 2
    My guess is for performance reasons. Like what happens when you try to change a lightning:input type dynamically. It's simply not supported. I don't know if they'll ever get it fixed, but for now, I think you might need to do dynamic form creation. – sfdcfox May 2 '18 at 16:32
  • @sfdcfox trying to $A.create the recordEditForm dynamically, but as you assumed: not working easily. Will update my question with the code soon... – Uwe Heim May 2 '18 at 16:35
  • @sfdcfox as you suggested and as you predicted: see my $A.create-updates above. Result is nothing, which isn't much. Obviously I'm doing it wrong. – Uwe Heim May 2 '18 at 16:47
8

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

  • oh ja, that rocks! thx a lot! So as a take away it seems, that we have only ONE shot on objectApiName and can not change it later. So we have to shoot late to hit. – Uwe Heim May 2 '18 at 17:14
  • You probably can.. but it might go ugly. Make ParentType as null first, it will destroy the inner component and then set ParentType as something new, it will help you create with a different type. – Pranay Jaiswal May 2 '18 at 17:16
  • the null-trick is not working here. I have tried it, see my comments above: null makes it to enter create-new-record-mode, which once initiated, can not be left by providing a good recordId plus objectApiName later on... sad but true. – Uwe Heim May 2 '18 at 17:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.