LWC : How to set default values when you clone a record?

Problem: Currently we have no standard way to populate default values while cloning records in salesforce. For eg. If I want to clone a Case record, I have a requirement to set Status field as Open on UI while cloning.

Approach: We will be using create new record page feature of lwc to implement this feature. Lets dive dive into the steps-

1.Create a metadata where we will define which fields to populate

2. Now write a method to populate Default-

/*populate defaultValues While Cloning Record*/
private static void populateDefaultValues(sObject newSobj, String objectAPIName) {
	String recordTypeId;
	String strRecordTypeName = '';
	try {
		recordTypeId = String.valueOf(newSobj.get('RecordTypeId'));
		strRecordTypeName = Schema.getGlobalDescribe().get(objectAPIName).getDescribe().getRecordTypeInfosById().get(recordTypeId).getDeveloperName();
	} catch(Exception e) {
		//if object don't have record type, you will get an error and I don't want to break the flow thats why added this piece of code inside try catch. 
		system.debug(e.getMessage());
	}
	//query metadata to populate data set in metadata based on object name
	list<Custom_Clone_Default__mdt> lstMdtRec = [SELECT Id, Label, Field_Value__c, Object_Name__c, Record_Type_Name__c 
												 FROM Custom_Clone_Default__mdt  WHERE Object_Name__c  = :objectAPIName
												AND Record_Type_Name__c =:strRecordTypeName];
	for (Custom_Clone_Default__mdt oMetaData : lstMdtRec){
		Schema.DescribeFieldResult dfr = fieldMap.get(oMetaData.Label).getDescribe();
		Schema.DisplayType t = dfr.getType();
		if(oMetaData.Field_Value__c == 'CURRENTUSER'){
			newSobj.put(oMetaData.Label, userinfo.getUserId());
		}else{
			if(t == DisplayType.Boolean){
				newSobj.put(oMetaData.Label, !String.isBlank(oMetaData.Field_Value__c) ? boolean.valueOf(oMetaData.Field_Value__c) : false);
			}else if(t == DisplayType.Date){
				newSobj.put(oMetaData.Label, !String.isBlank(oMetaData.Field_Value__c) ? date.valueOf(oMetaData.Field_Value__c) : null);
			} else if(t == DisplayType.DateTime){
				newSobj.put(oMetaData.Label, !String.isBlank(oMetaData.Field_Value__c)? datetime.valueOf(oMetaData.Field_Value__c) : null);
			}else{
				newSobj.put(oMetaData.Label, oMetaData.Field_Value__c);
			}
		}
	}
}

3. Write a method to fetch current record with all unique editable fields except createdbyid, lastmodifedid, lastmodifieddate and createddate field and make fields as default using method ‘populateDefaultValues’

@AuraEnabled
public static sObject getClonedValues(Id recordId, String objectAPIName) {
	Map<String, Schema.SobjectType> schemaMap = Schema.getGlobalDescribe();
	fieldMap = schemaMap.get(objectAPIName).getDescribe().fields.getMap();
	String soqlQuery = 'Select ';
	Schema.DescribeFieldResult fieldResult;
	Set<String> ignoreFields = new Set<String>{'createdbyid', 'lastmodifiedbyid', 'lastmodifieddate', 'createddate'};
	for(String s : fieldMap.keySet()) {
		if(!ignoreFields.contains(s)) {
			fieldResult = fieldMap.get(s).getDescribe();
			if(fieldResult.isCreateable() && !fieldResult.isUnique()) {
				soqlQuery += s + ',';
			}
		}
	}

	soqlQuery = soqlQuery.removeEnd(',');
	soqlQuery += ' FROM ' + objectAPIName + ' WHERE ID = \'' + recordId + '\'';
	Sobject record = Database.query(soqlQuery);
	populateDefaultValues(record, objectAPIName);
	try {
		return record;
	} catch(Exception e) {
		return null;
	}
}

You can refer my blog to check how we can get record type Id, developer name and label using apex.

Now write a lwc component, which will use ‘getClonedValues’ method to create map which we will pass inside create new page as shown below-

import { LightningElement, api, wire } from 'lwc';
import { encodeDefaultFieldValues } from 'lightning/pageReferenceUtils';
import { NavigationMixin } from 'lightning/navigation';
import getClonedValues from '@salesforce/apex/CloneSingleRecord.getClonedValues';
export default class Custom_clone_lwc extends NavigationMixin(LightningElement) {
		@api recordId;
		@api objectApiName;
		recordTypeId;
		
    @api async invoke() {
        this.fetchClonedValues(this.recordId, this.objectApiName);
    }

    fetchClonedValues(recId, objectName) {
				getClonedValues({ recordId: recId, objectAPIName : objectName }).then(data => {
            const map = new Map(Object.entries(data));
            let keys = Object.keys(data);
            let finalObj = {};
            for (let i = 0; i < keys.length; i++) {
                var skey = keys[i];
                var val =  map.get(keys[i]);
								if(val) {
                	finalObj[skey] = val;
								}
            }
						if(finalObj['RecordTypeId']) {
								this.recordTypeId = finalObj['RecordTypeId'];
						}
						
						console.log('finalObj>>'+ JSON.stringify(finalObj));
            const defaultValues = encodeDefaultFieldValues(finalObj);
						
						let self = this;
            this[NavigationMixin.Navigate]({
                type: 'standard__objectPage',
                attributes: {
                    objectApiName: objectName,
                    actionName: 'new'
                },
                state: {
                    defaultFieldValues: defaultValues,
										recordTypeId: self.recordTypeId
                }
            });
        }).catch(error => {
            console.log('error>> ' + JSON.stringify(error));
        });
    }
}

Make this component compatible with quick action using xml as shown below

<?xml version="1.0"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
	<apiVersion>56</apiVersion>
	<isExposed>true</isExposed>
	<targets>
		<target>lightning__RecordAction</target>	
	</targets>	
	<targetConfigs>
		<targetConfig targets="lightning__RecordAction" >
				<actionType>Action</actionType>
		</targetConfig>
	</targetConfigs>
</LightningComponentBundle>

Check out my below blog post also to find out how we can fetch record type information in apex using various ways-

Published by Sandeep Kumar

He is a Salesforce Certified Application Architect having 11+ years of experience in Salesforce.

Leave a Reply