What if you want to create a child record from the parent record page in lightning? What is most feasible and quick solution here?
Of course Lightning Object Specific Quick Action, right?
Well, there is a small issue, what if you want to select the “Record Type” before creating the record? Yes, object-specific out of the box quick action does not support dynamic record type selection, however, you can select the default record type when creating the quick action.
So what is the solution here? 🤔
The solution is to create a Lightning Component which allows you to select the record type before creating the record. So let’s create our component which:
- Can be added as Quick Action
- Should allow us to select available “Record Types”
- Should allow us to create the child record
Scenario: We need a quick action on Account record detail page, which can create child Contact records using selected record type.
Your code should look like this
RecordTypeSelector.apxc
public class RecordTypeSelector{ /* * This function will fetch the RecordTypes of * provided object and will return a map of * recordTypeId and recordTypeNames * it excludes 'master' record type * */ public static Map<Id, String> recordtypemap; @AuraEnabled public static Map<Id, String> fetchRecordTypeValues(String objectName){ List<Schema.RecordTypeInfo> recordtypes = Schema.getGlobalDescribe().get(objectName).getDescribe().getRecordTypeInfos(); recordtypemap = new Map<Id, String>(); for(RecordTypeInfo rt : recordtypes){ if(rt.getName() != 'Master' && rt.getName().trim() != '') recordtypemap.put(rt.getRecordTypeId(), rt.getName()); } return recordtypemap; } }
CreateContactWithRecordType.cmp
<aura:component controller="RecordTypeSelector" implements="force:lightningQuickActionWithoutHeader,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global" > <aura:attribute name="lstOfRecordType" type="String[]" /> <aura:attribute name="mapOfRecordType" type="Map" /> <!-- Fetch all available record types after component construction but before rendering --> <aura:handler name="init" value="{!this}" action="{!c.fetchListOfRecordTypes}"/> <lightning:layout multipleRows="true" horizontalAlign="center"> <lightning:layoutItem flexibility="auto" padding="around-small" size="12" largeDeviceSize="12" mediumDeviceSize="12" smallDeviceSize="12"> <lightning:formattedText value="Select Contact Record Type" /> </lightning:layoutItem> <lightning:layoutItem flexibility="auto" padding="around-small" size="12" largeDeviceSize="12" mediumDeviceSize="12" smallDeviceSize="12"> <!-- select to hold all available record type names list --> <lightning:select aura:id="recordTypePickList" name="selectRecordType" label="Select a Record Type"> <option value="" text="Select Record Type"/> <aura:iteration items="{!v.lstOfRecordType}" var="item"> <option value="{!item}" text="{!item}"/> </aura:iteration> </lightning:select> </lightning:layoutItem> <lightning:layoutItem flexibility="auto" padding="around-small" size="3" largeDeviceSize="3" mediumDeviceSize="3" smallDeviceSize="6"> <lightning:button variant="brand" label="Next" onclick="{!c.createRecord}"/> </lightning:layoutItem> <lightning:layoutItem flexibility="auto" padding="around-small" size="3" largeDeviceSize="3" mediumDeviceSize="3" smallDeviceSize="6"> <lightning:button variant="neutral" label="Cancel" onclick="{!c.closeModal}" /> </lightning:layoutItem> </lightning:layout> </aura:component>
CreateContactWithRecordTypeController.js
({ /* * This method is being called from init event * to fetch all available recordTypes * */ fetchListOfRecordTypes: function(component, event, helper) { var action = component.get("c.fetchRecordTypeValues"); //pass the object name here for which you want //to fetch record types action.setParams({ "objectName" : "Contact" }); action.setCallback(this, function(response) { var mapOfRecordTypes = response.getReturnValue(); component.set("v.mapOfRecordType", mapOfRecordTypes); var recordTypeList = []; //Creating recordTypeList from retrieved Map for(var key in mapOfRecordTypes){ recordTypeList.push(mapOfRecordTypes[key]); } if(recordTypeList.length == 0){//Object does not have any record types //Close Quick Action Modal here helper.closeModal(); //Calling CreateRecord modal here without providing recordTypeId helper.showCreateRecordModal(component, "", "Contact"); } else{ component.set("v.lstOfRecordType", recordTypeList); } }); $A.enqueueAction(action); }, /* * This method will be called when "Next" button is clicked * It finds the recordTypeId from selected recordTypeName * and passes same value to helper to create a record * */ createRecord: function(component, event, helper, sObjectRecord) { var selectedRecordTypeName = component.find("recordTypePickList").get("v.value"); if(selectedRecordTypeName != ""){ var selectedRecordTypeMap = component.get("v.mapOfRecordType"); var selectedRecordTypeId; //finding selected recordTypeId from recordTypeName for(var key in selectedRecordTypeMap){ if(selectedRecordTypeName == selectedRecordTypeMap[key]){ selectedRecordTypeId = key;//match found, set value in selectedRecordTypeId variable break; } } //Close Quick Action Modal here helper.closeModal(); //Calling CreateRecord modal here without providing recordTypeId helper.showCreateRecordModal(component, selectedRecordTypeId, "Contact"); } else{ alert('You did not select any record type'); } }, /* * closing quickAction modal window * */ closeModal : function(component, event, helper){ helper.closeModal(); } })
CreateContactWithRecordTypeHelper.js
({ /* * This methid takes recordTypeId and entityTypeName parameters * and invoke standard force:createRecord event to create record * if recordTypeIs is blank, this will create record under master recordType * */ showCreateRecordModal : function(component, recordTypeId, entityApiName) { debugger; var createRecordEvent = $A.get("e.force:createRecord"); if(createRecordEvent){ //checking if the event is supported if(recordTypeId){//if recordTypeId is supplied, then set recordTypeId parameter createRecordEvent.setParams({ "entityApiName": entityApiName, "recordTypeId": recordTypeId, "defaultFieldValues": { "AccountId": component.get("v.recordId") } }); } else{//else create record under master recordType createRecordEvent.setParams({ "entityApiName": entityApiName, "defaultFieldValues": { "AccountId": component.get("v.recordId") } }); } createRecordEvent.fire(); } else{ alert('This event is not supported'); } }, /* * closing quickAction modal window * */ closeModal : function(){ var closeEvent = $A.get("e.force:closeQuickAction"); if(closeEvent){ closeEvent.fire(); } else{ alert('force:closeQuickAction event is not supported in this Ligthning Context'); } }, })
Create a Lightning Component Quick Action
Once you have developed your lightning component, you need to add this as a quick action on Account Object. Follow this gif to create a new quick action named “Create Contact” using CreateContactWithRecordType component:
Add your quick action to Account Page Layout
Now add your quick action “Create Contact” to Account Object page layout to display this action on the page. Follow this gif:
What we did
- We have created an Apex Controller RecordTypeSelector to fetch all available record type list.
- We have implemented force:lightningQuickActionWithoutHeader interface in our component to make it available for use as quick action.
- We have created lightning:select component to hold record type list.
- We have used force:createRecord standard event to create a new record.
- We have passed “recordTypeId” parameter to force:createRecord event to create a record with selected record type name.
- We have supplied “defaultFieldValues” parameter to pass AccountId to new Contact Record.
- Created new lightning component quick action named “Create Contact”.
- Added quick action to Account Page layout.
Output
The code should be self-explanatory as I have added comments everywhere. Please post a comment if you have any queries.
That’s it guys!! Your new lightning component is ready and available for use. 🤗
Arav
4 May 2018Hi Manish, it helped a lot. I have a trouble in one scenario, I hope you can help me. please send an email to ak144.sfdc@gmail.com, so that I can be in contact with you.
Manish Choudhari
4 May 2018Hi Arav,
I am glad that it helped. Please do reach out to me at https://www.linkedin.com/in/manish-choudhary/
Keyur Shah
5 May 2018How to hide default popup of Quick Action.One Default Dialogue box is populated automatically when we are clicking on Quick Action.
Manish Choudhari
6 May 2018Hey Keyur,
Which interface you are using:
1. force:lightningQuickActionWithoutHeader
2. force:lightningQuickAction
The first one allows a component to display in a panel without additional controls. The component should provide a complete user interface for the action.
The 2nd additional controls as well.
You can only use one of these at a time.
In your case, you should use force:lightningQuickActionWithoutHeader.
Mayank Jain
1 Aug 2018Hey Manish ,
I have a small question here . I don’t want to open the whole edit page while creating the record but rather a small layouts with only fields require to create a new record . So lets say I create a GLobal quick action with a layout and I need to call the layout of the global action from Lightning Component . Opening the whole page on mobile , its not very justified
Manish Choudhari
15 Aug 2018There are 2 ways to do that:
1. Either use your own custom component to create a record and limit the number of fields there.
2. Or if you are using standard event (like in this example), you need to limit the field on page layout associated with the record type. The only problem with this solution is, it will limit the number of field even on the web browser (not only mobile browser)
sandeep
22 Aug 2018Hi Manish,
I have a small issue here, you are pre populate the account name but, I want to populate other fields also can you please suggest me how to do that.
Manish Choudhari
23 Aug 2018Yes Sandeep,
You can prepopulate any field of same object which you are creating. In my case, I was creating “Contact” record, so I can populate any contact field.
Shivam Mishra
5 Jul 2019Hi Manish,
could you please suggest us how to pre-populate the value in contact field from account field not accountId field on contact? Example: We have phone field on account and contact so when we click on action button and after record type selection of contact, account phone number should populate in contact phone field during record creation.
Thanks.
Manish Choudhari
5 Jul 2019Hi Shivam,
You can use defaultFieldValue property to pass Phone number from Account to Contact.
"defaultFieldValues": {
}
"AccountId": component.get("v.recordId"),
"Phone" :
Behzad Habibi
19 Sep 2018Hi Manish,
I have a small issue after creating an event from the opportunity, the Event is not shown on activity related to the Opportunity.
Would you please help me in this issue.
Manish Choudhari
19 Sep 2018Hi Behzad,
Events are treated in a different way in Salesforce. Can you check in newly created event if WhatId matches to opportunity id.
Behzad Habibi
20 Sep 2018Hi Manish,
yes, WhatId is matching to the Opportunity Id.
Thank you very much for your help.
Behzad Habibi
20 Sep 2018Hi Manish,
As I am completely new with coding I need again your help,
I am going to write an Apex Class but I am getting an error I would appreciate if you take a look in the below.
@isTest
public class recordtypeSelectorTest {
static testmethod void testFetchRecordTypes() {
List values = recordtypeselector.fetchRecordTypeValues();
}
static testmethod void testgetRecordTypeId() {
String recordTypeLabel = ‘Consultant Termin’;
ID testId = recordtypeSelector.getRecTypeId(recordTypeLabel);
System.assert(testId != null);
}
}
Error: Compile Error: Method does not exist or incorrect signature: void fetchRecordTypeValues() from the type RecordTypeSelector at line 5 column 46
Manish Choudhari
20 Sep 2018You need to pass object name to function “fetchRecordTypeValues”. Change your line 5 to something like below:
recordtypeselector.fetchRecordTypeValues(‘Account’);
-Replace ‘Account’ with your object name.
Priyamvada Sharma
23 Sep 2018Hi,
Can we disable or hide the ‘Save and New’ button shown on the create record popup?’
Manish Choudhari
25 Sep 2018That is the standard component and cannot be modified.
Ralf
28 Sep 2018Works fine – thank you for the code and the detailed instructions. However: The RecordType selector ignores the profile settings for available record types. Only when saving the new record, the user gets an error message telling him that the record type id isn’t valid for the user.
How could I fix that?
Manish Choudhari
1 Oct 2018Hi Ralf,
You Apex Code runs in system context hence it is giving all the record type even when the user does not have access to that record type. However, you can avoid this by using sample code below:
List recordTypes = new List ();
for(RecordTypeInfo info: Account.SObjectType.getDescribe().getRecordTypeInfos()) {
if(info.isAvailable()) {
recordTypes.add(new SelectOption(info.getRecordTypeId(), info.getName()));
}
}
// Include these next 3 lines to output the results during development and debugging
for( SelectOption selopt: recordTypes ) {
System.debug( selopt.getLabel() + ',' + selopt.getValue() );
}
Mahesh L
11 Dec 2018HI Manish,
How can auto redirect recordtype based on parentb field.lets say i have parent object Lead having picklist field with value A ,B,C.
Lead_Age__c is a custom object which is lookup with Lead,and having 2 record type (New Value,Old Value)..
When I am creating child from parent there is option for record type select.But need to redirect directly recordtype page based on parent picklist value.No need to select any record type.If picklsit value A then at the time of child creation (when click on new button in child it auto direct New value Record type directly and like this.)
Manish Choudhari
25 Jan 2019This can be done Mahes. While calling e.force.ceateRecord, just pass the recordTypeId you are getting from lead object and skip recordType selection page.
Prakash
25 Jan 2019When we use event “e.force:createRecord” for the selected Object(Contact) can we select few fields rather default pagelayout?
Manish Choudhari
25 Jan 2019You may have to override the “New Contact” page for contact object.
Sanjana
13 Apr 2019Hi Manish,
I have tried the above code . My Scenario is that the quick action is for a target custom object. When am selected the record type during quick action am getting this error.
“No access to field AccountId. Either the field was removed from the entity or access to this field was removed.”
Can you please help me out, It is creating hell in production environment , awaiting for your reply
jagadeesh
23 May 2019Hi Sanjana,
I think you are using AccountID field which is related to contact object. So please check the custom object lookup field API name(Account__c).
Pingback: MultiSelect Dropdown Aura Component - SFDCFacts
Fritzie Say
16 May 2019Hello,
Is there any sample class of your Apex Class? Thank yo.
Manish Choudhari
8 Jun 2019Class code is already posted in this blog.
Mike Gustafson
24 Jun 2019Hi Manish,
This is a great piece of code and works great. Question about setting default values in the helper class. How can you set the values of a lookup field that are both on the parent and child object. Say both objects have related Opportunity field and I want to set the Opportunity value on the child from the parent.
Something like this, which does not work.
“cust__Related_Opportunity__c”: component.get(“v.Related_Opportunity__c”)
Many Thanks,
Mike
sowmya
8 Jul 2019Hi Mike,
Did You get any workaround for this. Even I have the same problem in getting default values. Please let me know if you have any solution for this.
Regards,
Sowmya N
Vinay
1 Aug 2019Doesn’t work for me either. It does not pull any value from the record. Looking for a solution. Would appreciate if someone could assist.
Regards
Vinay
Heather
25 Oct 2019I’m having trouble with my test class…not sure what I’m doing wrong, but I’m getting the following errors:
Illegal assignment from Map to List (line 6)
Method does not exist or incorrect signature: void getRecTypeId(String) from the type RecordTypeSelector (line 11)
My code below. Instead of Account, I’m using Asset and instead of Contact, I’m using customer object OrderProject__c. Avaya is a record type value.
I’ve only created a few test classes so far, and never for a lightning so if you could help me out on this one, I’d really appreciate it!
@isTest
public class TestRecordTypeSelector {
static testmethod void testFetchRecordTypes() {
List values = RecordTypeSelector.fetchRecordTypeValues(‘Asset’);
}
static testmethod void testgetRecordTypeId() {
String recordTypeLabel = ‘Avaya’;
ID testId = RecordTypeSelector.getRecTypeId(recordTypeLabel);
System.assert(testId != null);
}
}
Jonathan DUSHIMIMANA
17 Nov 2019Hello Manish,
I did write the code provided and it works fine in the sandbox. However when I tried to deploy the RecordTypeSelector Apex class to production, it said that my code coverage is below 75%, which means I have to test it before deploying it. My class look as below;
public class RecordTypeSelection {
/*
* This function will fetch the RecordTypes of
* provided object and will return a map of
* recordTypeId and recordTypeNames
* it excludes ‘master’ record type
* */
public static Map recordtypemap;
@AuraEnabled
public static Map fetchRecordTypeValues(String objectName){
List recordtypes = Schema.getGlobalDescribe().get(objectName).getDescribe().getRecordTypeInfos();
recordtypemap = new Map();
for(RecordTypeInfo rt : recordtypes){
if(rt.getName() != ‘Master’ && rt.getName().trim() != ”)
recordtypemap.put(rt.getRecordTypeId(), rt.getName());
}
return recordtypemap;
}
}
I tried using the below class for testing but it seems to give errors and can’t test, hence my code coverage can’t increase. any help you can provide? The below is the test class I am trying to use which is giving me errors;
@isTest
public class recordtypeSelectorTest {
@isTest
static testmethod void testFetchRecordTypes() {
List values = recordtypeselection.fetchRecordTypeValues(‘Donor_Events__c’);
}
@isTest
static testmethod void testgetRecordTypeId() {
String recordTypeLabel = ‘Donor_Events__c’;
ID testId = recordtypeSelection.fetchRecordTypeValues(recordTypeLabel);
System.assert(testId != null);
}
}