LWC Project: Update Parent Records from Child Page.

What we are building today?

A Lighting web component which will allow user to update parent details from child record. 

What does that mean? Example:  On Opportunity we have custom parents object Parent Object 1, Parent Object 2  and we want to update their details from child(Opportunity) it self.

NOTE:  Using standard Related Record component we can update parent record details from child.

Watch a quick demo

LWC component to update parent record from child

What you will learn?

  • Get parent objects of any custom or standard object using apex (get all parents of Accounts)
  • Get all updatable fields of any salesforce object
  • Dynamically get label of any custom or standard object in Apex
  • Using Schema.getGlobalDescribe() method
  • How to use lightning-record-edit-form and display fields dynamically
  • Dynamically pass current object name and record to lightning-record-edit-form
  • Passing options to lightning-combobox dynamically
  • How to use uiRecordApi of LDS
  • Get record details using getRecord of LDS uiRecordApi
  • Dynamically use ShowToastEvent to display Success & Error popups

We have covered all the about topics in this post, now lets dig to each of these. Find the combined version at last, copy and paste it to see in demo action.

Get all parent of any salesforce object dynamically:

@AuraEnabled
public static Map < String, String > getParentObjects(String childObject) {
  Map < String, String > mapParentObjects = new Map < String, String > ();
  String SobjectApiName = childObject;
  Map < String, Schema.SObjectType > schemaMap = Schema.getGlobalDescribe();
  for (Schema.SobjectField fldMap: schemaMap.get(SobjectApiName).getDescribe().fields.getMap().Values()) {
    String customField = fldMap.getDescribe().getName();
    if (customField.endsWith('__c') && fldMap.getDescribe().getType() == Schema.DisplayType.REFERENCE) {
      List < SObjectType > refObjs = fldMap.getDescribe().getReferenceTo();
      String objectName = String.valueOf(refObjs[0]);
      mapParentObjects.put(getObjectLabel(objectName), objectName);
    }
  }
  return mapParentObjects;
}

Dynamically get all updatable fields of any object in Apex:

@AuraEnabled
public static Map < String, String > getUpdatableFields(String strObjectName) {
  Map < String, Schema.SObjectType > detail = Schema.getGlobalDescribe();
  Map < String, String > mapOfPicklistTypeFields = new Map < String, String > ();
  for (Schema.SObjectField fields: detail.get(strObjectName).getDescribe().fields.getMap().Values()) {
    If(fields.getDescribe().isAccessible() && fields.getDescribe().isCreateable() && fields.getDescribe().isUpdateable()) {
      mapOfPicklistTypeFields.put(fields.getDescribe().getLabel(), fields.getDescribe().getName());
    }
  }
  return mapOfPicklistTypeFields;
}

Method of get object label:

public static String getObjectLabel(String objectName) {
  Map < String, SObjectType > sObjects = Schema.getGlobalDescribe();
  return sObjects.get(objectName).getDescribe().getLabel();
}

Fetch current record object name and record Id in LWC:

Use @api recordId and @api objectApiName to get current record Id and object name in lighting component.

IMP NOTE: Sometimes you may get undefined value in both of the about property, just remove the component from page insert it again to fix it.

@api recordId;  
@api objectApiName;

Complete code to build parent record updater

Apex Class:

public with sharing class UpdateParentFromChild {
  @AuraEnabled
  public static Map < String, String > getParentObjects(String childObject) {
    Map < String, String > mapParentObjects = new Map < String, String > ();
    String SobjectApiName = childObject;
    Map < String, Schema.SObjectType > schemaMap = Schema.getGlobalDescribe();
    for (Schema.SobjectField fldMap: schemaMap.get(SobjectApiName).getDescribe().fields.getMap().Values()) {
      String customField = fldMap.getDescribe().getName();
      if (customField.endsWith('__c') && fldMap.getDescribe().getType() == Schema.DisplayType.REFERENCE) {
        List < SObjectType > refObjs = fldMap.getDescribe().getReferenceTo();
        String objectName = String.valueOf(refObjs[0]);
        mapParentObjects.put(getObjectLabel(objectName), objectName);
      }
    }
    return mapParentObjects;
  }

  public static String getObjectLabel(String objectName) {
    Map < String, SObjectType > sObjects = Schema.getGlobalDescribe();
    return sObjects.get(objectName).getDescribe().getLabel();
  }
  @AuraEnabled
  public static Map < String, String > getUpdatableFields(String strObjectName) {
    Map < String, Schema.SObjectType > detail = Schema.getGlobalDescribe();
    Map < String, String > mapOfPicklistTypeFields = new Map < String, String > ();
    for (Schema.SObjectField fields: detail.get(strObjectName).getDescribe().fields.getMap().Values()) {
      If(fields.getDescribe().isAccessible() && fields.getDescribe().isCreateable() && fields.getDescribe().isUpdateable()) {
        mapOfPicklistTypeFields.put(fields.getDescribe().getLabel(), fields.getDescribe().getName());
      }
    }
    return mapOfPicklistTypeFields;
  }
}

LWC HTML:

<template>
    <div class="slds-card">
        <lightning-combobox class="slds-grid slds-col slds-size_2-of-3"
                name="parentObjects"
                label="Select Parent Object"
                value={value}
                placeholder="Select Object"
                options={options}
                onchange={handleParentObjectChange} >
        </lightning-combobox>
  
        <div lwc:if={showEditForm} >
            <lightning-record-edit-form object-api-name={parentObject} record-id={parentRecordId} onsuccess={handleSuccess}  onerror={handleError}>
                <template for:each={fieldsToUpdate} for:item="currentItem" for:index="index">
                    <!-- <lightning-layout-item key={currentItem.value} size="6" class="slds-grid">
                        <lightning-input-field field-name={currentItem.value}> </lightning-input-field>
                    </lightning-layout-item> -->
                    <lightning-input-field  key={currentItem.value} field-name={currentItem.value}> </lightning-input-field>
                </template>
                <div class="slds-var-m-top_medium">
                    <lightning-button variant="brand" type="submit"  label="Update"></lightning-button>
                </div>
            </lightning-record-edit-form>
        </div>
    </div>
    
</template>

LWC JS:

import { LightningElement, track, api, wire } from 'lwc';
import { ShowToastEvent } from "lightning/platformShowToastEvent";
import getParentObjects from '@salesforce/apex/UpdateParentFromChild.getParentObjects';
import getUpdatableFields from '@salesforce/apex/UpdateParentFromChild.getUpdatableFields';
import { getRecord } from "lightning/uiRecordApi";

export default class UpdateParentFromChild extends LightningElement {

    @track options = [];
    @track fieldsToUpdate = [];
    @track showEditForm = false;
    @api recordId;  
    @api objectApiName;
    @track parentRecordId;  
    @track childRecordData;
   
    connectedCallback() { 
        this.getParentObjects();
        this.childRecordId = this.recordId;
        console.log('objectApiName 1' , this.objectApiName);
    }

    @wire(getRecord, { recordId: "$childRecordId", layoutTypes: ["Full"], modes: ["View"] })
    childData({ error, data }) {
        if (data) {
            this.childRecordData = data;
        } else ( 
            console.log('error in getting company url',error)
        )
    }  
    
    getParentObjects() {
        getParentObjects({ childObject: this.objectApiName })
            .then(result => {
                this.options = [];
                for (let key in result ) {
                    this.options.push({ label: key, value: result[key] });
                }
            })
            .catch(error => {
                this.showToast('Error', 'Error getting parent objects', 'error')
        });
    }

    handleParentObjectChange(event) { 
        this.showEditForm = false;
        this.parentRecordId = '';
        this.parentObject = event.detail.value;
        this.parentRecordId = this.childRecordData.fields[this.parentObject].value
        this.getUpdatableFields(this.parentObject);
    }
    getUpdatableFields() {
        getUpdatableFields({ strObjectName: this.parentObject})
            .then(result => {
                this.fieldsToUpdate = [];
                for (let key in result ) {
                    this.fieldsToUpdate.push({ label: key, value: result[key] });
                }
                if(this.parentRecordId && this.parentRecordId != '') { 
                    this.showEditForm = true;
                } else { 
                    this.showToast('Warning', 'Parent not linked', 'warning')
                }
                
            })
            .catch(error => {
                this.showToast('Error', 'Error getting updatable fields', 'error')
        });
    }

    handleSuccess() { 
        this.showToast('Success', 'Child record updated', 'success')
    }
    
    handleError() { 
        this.showToast('Error', 'Error in updating child record', 'error')
    }

    showToast(title, message, variant) {
        const evt = new ShowToastEvent({
          title: title,
          message: message,
          variant: variant,
        });
        this.dispatchEvent(evt);
    }

}

LWC XML:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>62.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>Update Parent Record</masterLabel>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>
We believe you have learnt and touch many important LWC concepts today. Build another amazing LWC project :  Custom Record Fetcher Component with Salesforce lighting web components.

Leave a Reply