CREATE CUSTOM FIELDS AND CUSTOM OBJECTS WITH LWC AND APEX
- S. Chaudhary
- Sep 23, 2023
- 2 min read
Updated: Nov 8, 2023

In Salesforce, users have the option of creating custom objects and their fields via two main routes. The primary and more straightforward method is by utilizing the Object Manager's user interface, where you simply click on the "New" button and define the object as needed. Alternatively, for a more dynamic approach, users can leverage the power of code to achieve this task. In this context, the MetadataService class becomes our tool of choice.
Salesforce's Metadata API provides developers with a versatile means of interfacing with the platform's metadata. By leveraging this API, one can manage and customize various aspects of their Salesforce instance. In today's guide, we'll specifically focus on how you can utilize the `MetadataService` class to create custom objects.
1. Preparing the MetadataService:
Before delving into the code:
Ensure the MetadataService class is present in your Salesforce org.
This class is available on the FinancialForce GitHub repository.
Once acquired, deploy the class to your Salesforce org using tools such as the Salesforce CLI or Workbench.
2. Grasping the MetadataService:
MetadataService acts as a wrapper for Salesforce's Metadata API. This allows for operations like creation, retrieval, modification, and deletion of metadata items including but not limited to Custom Objects, Layouts, and Custom Fields, directly from Apex.
3. Introducing the MetadataServiceHandler Class:
To facilitate the creation of custom objects, we'll make use of a helper class, MetadataServiceHandler, which will in turn utilize the standard methods from MetadataService class.
MetaDataServiceHandler.cls
public with sharing class MetaDataServiceHandler {
public static void createObject(ObjectWrapper data) {
MetadataService.MetadataPort metadataservice = new MetadataService.MetadataPort();
metadataservice = createService();
List<MetadataService.CustomObject> objectList = new List<MetadataService.CustomObject>();
MetadataService.CustomObject customobject = new MetadataService.CustomObject();
customobject.fullName = data.objectAPI;
customobject.label = data.objectName;
customobject.pluralLabel = data.objectPlural;
customObject.nameField = new MetadataService.CustomField();
customobject.nameField.type_x = 'Text';
customobject.nameField.label = 'Name';
customobject.deploymentStatus = 'Deployed';
customObject.sharingModel = 'ReadWrite';
objectList.add(customobject);
metadataservice.createMetadata(objectList);
}
public static List<MetadataService.SaveResult> createDifferentTypeField(List<fieldWrapper> fieldWrappers, String objectName) {
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service = createService();
List<MetadataService.Metadata> dataToInsert = new List<MetadataService.Metadata>();
for (fieldWrapper currentField : fieldWrappers) {
dataToInsert.add(getFieldByType(currentField, objectName));
}
List<MetadataService.SaveResult> results = service.createMetadata(dataToInsert);
return results;
}
public static MetadataService.MetadataPort createService(){
MetadataService.MetadataPort service = new MetadataService.MetadataPort();
service.SessionHeader = new MetadataService.SessionHeader_element();
service.SessionHeader.sessionId = GetSessionIdController.getSessionIdFromVFPage(Page.SessionId);
return service;
}
public static MetadataService.CustomField getFieldByType(fieldWrapper currentField, string objectName) {
MetadataService.CustomField field = new MetadataService.CustomField();
field.fullName = objectName + '.' + currentField.apiName;
field.label = currentField.label ;
if (currentField.type == 'text') {
field.length = Integer.valueOf(currentField.textLength);
field.type_x = 'Text';
}
else if (currentField.type == 'number') {
field.precision = Integer.valueOf(currentField.textLength);
field.scale = Integer.valueOf(currentField.scale);
field.type_x = 'Number';
field.unique = false;
}
else if (currentField.type == 'checkbox') {
field.defaultvalue = currentField.defaultvalue;
field.type_x = 'Checkbox';
}
else if (currentField.type == 'date') {
field.type_x = 'Date';
}
else if (currentField.type == 'lookup') {
field.type_x = 'Lookup';
field.relationshipLabel = currentField.relationshipLabel;
field.relationshipName = currentField.relationshipName;
field.referenceTo = currentField.referenceTo;
}
else if (currentField.type == 'longTextArea') {
field.type_x = 'LongTextArea';
field.length = Integer.valueOf(currentField.textLength);
field.visibleLines = Integer.valueOf(currentField.visibleLines);
}
else if (currentField.type == 'masterdetail') {
field.type_x = 'MasterDetail';
field.relationshipLabel = currentField.relationshipLabel;
field.relationshipName = currentField.relationshipName;
field.referenceTo = currentField.referenceTo;
}
return field;
}
}
Here's a concise explanation of the key components:
fullName: Represents the API name of your object. Custom objects should always end with ‘__c’.
label & pluralLabel: These dictate how the object will be displayed within Salesforce's UI.
deploymentStatus: When set to 'Deployed', the object becomes readily available. If not, it stays in a developmental phase.
sharingModel: This is an indicator of the data access settings. A 'ReadWrite' status allows users to both view and modify the data by default.
referenceTo:- It will hold the related object API name.
Dive Deep into Methods:
createObject: Feed it an ObjectWrapper, and watch it birth a new object in your org.
createDifferentTypeField: Gift it a list of Field Wrappers and an Object name, and it'll craft fields based on type, right in your org.
createService: A hero in disguise! Since LWC doesn't spill the session id beans (security first, folks!), we'll fetch it through a visualforce page.
The different Classes that are involved in the successful execution of the service handler method are:
GetSessionIdController.cls
global with sharing class GetSessionIdController {
global static String getSessionIdFromVFPage(PageReference visualforcePage){
String content = visualforcePage.getContent().toString();
Integer s = content.indexOf('Start_Of_Session_Id') + 'Start_Of_Session_Id'.length(),
e = content.indexOf('End_Of_Session_Id');
return content.substring(s, e);
}
}
SessionId.page
<apex:page >
Start_Of_Session_Id{!$Api.Session_ID}End_Of_Session_Id
</apex:page>
SObjectDomain.cls
public with sharing class SObjectDomain {
public List<Schema.SObjectType> getSObjectApiNameToLabel() {
return Schema.getGlobalDescribe().values();
}
}
ObjectWrapper.cls
public class ObjectWrapper {
public string objectName;
public string objectAPI;
public string objectPlural;
public ObjectWrapper() {
}
}
SObjectProcessor.cls
public with sharing class SObjectProcessor {
public List<SObjectWrapper> getRelatedSObjects() {
List<SObjectWrapper> sobjectApiNameToLabel = new List<SObjectWrapper>();
for (Schema.SObjectType sobjectType : new SObjectDomain().getSObjectApiNameToLabel()) {
Schema.DescribeSObjectResult sobjectDescribe = sobjectType.getDescribe();
if (sobjectDescribe.isAccessible()) {
if (sobjectDescribe.isCustom() && !sobjectDescribe.isCustomSetting()) {
sobjectApiNameToLabel.add(
new SObjectWrapper(
sobjectDescribe.getLabel(),
sobjectDescribe.getName()
)
);
}
else if (Taggable_Standard_Object__mdt.getInstance(sobjectDescribe.getName().toUpperCase()) != null) {
sobjectApiNameToLabel.add(
new SObjectWrapper(
sobjectDescribe.getLabel(),
sobjectDescribe.getName()
)
);
}
}
}
return sobjectApiNameToLabel;
}
}
SObjectWrapper.cls
public with sharing class SObjectWrapper {
@AuraEnabled
public String label;
@AuraEnabled
public String value;
public SObjectWrapper(String label ,String apiName) {
this.label = label;
this.value = apiName;
}
}
createCustomFieldController.cls
public with sharing class createCustomFieldController {
@AuraEnabled(cacheable = true)
public static List<SObjectWrapper> getSObjects(){
try {
SObjectProcessor processor = new SObjectProcessor();
return processor.getRelatedSObjects();
}
catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
@AuraEnabled
public static List<MetadataService.SaveResult> createFields(String inputData, String objectName){
try {
List<fieldWrapper> fieldWrappers = new List<fieldWrapper>();
fieldWrappers = (List<fieldWrapper>)JSON.deserialize(inputData, List<fieldWrapper>.class);
return MetadataServiceHandler.createDifferentTypeField(fieldWrappers, objectName);
}
catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
}
createCustomObjectController.cls
public with sharing class createCustomObjectController {
@AuraEnabled
public static void createObject(String data ){
try {
ObjectWrapper objectData = new ObjectWrapper();
objectData = (ObjectWrapper)JSON.deserialize(data, ObjectWrapper.class);
MetaDataServiceHandler.createObject(objectData);
} catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
}
fieldWrapper.cls
public with sharing class fieldWrapper {
public String id;
public String label;
public String apiName;
public String type;
public String textLength;
public String defaultvalue;
public String relationshipLabel;
public String relationshipName;
public String referenceTo;
public String visibleLines;
public String scale;
public fieldWrapper() {
}
}
After Creating all these classes We need to create three LWC.
1. CreateCustomFields
CreateCustomFields.js
import { LightningElement, track, wire } from'lwc';
import getSObjects from'@salesforce/apex/createCustomFieldController.getSObjects';
import createFields from'@salesforce/apex/createCustomFieldController.createFields';
import { ShowToastEvent } from'lightning/platformShowToastEvent';
import FIELD_REMOVE_ERRORMESSAGE from'@salesforce/label/c.FIELD_REMOVE_ERRORMESSAGE';
import ERROR_MESSAGE from'@salesforce/label/c.ERROR_MESSAGE';
import REMOVE_THIS_FIELD from'@salesforce/label/c.REMOVE_THIS_FIELD';
import FIELD_LABEL from'@salesforce/label/c.FIELD_LABEL';
import FIELD_API_NAME from'@salesforce/label/c.FIELD_API_NAME';
import FIELD_TYPE from'@salesforce/label/c.FIELD_TYPE';
import TEXT_LENGTH from'@salesforce/label/c.TEXT_LENGTH';
import TEXT_LENGTH_LONGTEXT from'@salesforce/label/c.TEXT_LENGTH_LONGTEXT';
import VISIBLE_LINES from'@salesforce/label/c.VISIBLE_LINES';
import NUMBER_LENGTH from'@salesforce/label/c.NUMBER_LENGTH';
import DECIMAL_PLACES from'@salesforce/label/c.DECIMAL_PLACES';
import DEFAUL_VALUE from'@salesforce/label/c.DEFAUL_VALUE';
import RELATIONSHIP_LABEL from'@salesforce/label/c.RELATIONSHIP_LABEL';
import RELATIONSHIP_NAME from'@salesforce/label/c.RELATIONSHIP_NAME';
import RELETED_TO from'@salesforce/label/c.RELETED_TO';
import ADD_FIELD from'@salesforce/label/c.ADD_FIELD';
import CREATE_FIELDS from'@salesforce/label/c.CREATE_FIELDS';
import ERROR from'@salesforce/label/c.ERROR';
import SUCCESS from'@salesforce/label/c.SUCCESS';
import FIELD_CREATION_SUCCESS_MESSAGE from'@salesforce/label/c.FIELD_CREATION_SUCCESS_MESSAGE';
import FIELD_CREATION_ERROR_MESSAGE from'@salesforce/label/c.FIELD_CREATION_ERROR_MESSAGE';
import SUCCESS_TITLE from'@salesforce/label/c.SUCCESS_TITLE';
import ERROR_TITLE from'@salesforce/label/c.ERROR_TITLE';
import { getToRenderFieldType, getFieldValue, getFieldTypeOption, getCheckBoxDefalutOptions, getFormatedAPIName, hasSpecialCharacters } from'./customFieldHelper.js'
export default class CreateCustomFields extends LightningElement {
isObjectSelected = false;
SObjectList;
selectObjects = '';
errorFound = false;
errorMessage = '';
isRequired = true;
@track referenceObjectList;
@track allData = [
{
field: getFieldValue(),
componentType: getToRenderFieldType(),
isErrorFound : false,
fieldNumber : 1
}
]
label = {
REMOVE_THIS_FIELD :REMOVE_THIS_FIELD,
FIELD_LABEL : FIELD_LABEL,
FIELD_API_NAME : FIELD_API_NAME,
FIELD_TYPE : FIELD_TYPE,
TEXT_LENGTH : TEXT_LENGTH,
TEXT_LENGTH_LONGTEXT : TEXT_LENGTH_LONGTEXT,
VISIBLE_LINES : VISIBLE_LINES,
NUMBER_LENGTH : NUMBER_LENGTH,
DECIMAL_PLACES : DECIMAL_PLACES,
DEFAUL_VALUE : DEFAUL_VALUE,
RELATIONSHIP_LABEL : RELATIONSHIP_LABEL,
RELATIONSHIP_NAME : RELATIONSHIP_NAME,
RELETED_TO : RELETED_TO,
ADD_FIELD : ADD_FIELD,
CREATE_FIELDS : CREATE_FIELDS
}
@track fieldTypeOptions = getFieldTypeOption();
checkBoxDefaulOption = getCheckBoxDefalutOptions();
allFields =[];
scaleMaxValue;
isLoaded = true;
@wire(getSObjects) getSObjectsData({ data, error }) {
if (data) {
this.SObjectList = data;
}
else if (error) {
}
}
addField() {
let fieldtemp = getFieldValue();
fieldtemp.id = this.allData[this.allData.length-1].field.id + 1;
this.allData = [...this.allData, {
field: fieldtemp,
componentType: getToRenderFieldType(),
fieldNumber : fieldtemp.id + 1
}];
}
createFields() {
this.isLoaded = false;
for(let i=0; i<this.allData.length; i++){
this.allFields.push(this.allData[i].field);
}
createFields({inputData: JSON.stringify(this.allFields), objectName : this.selectObjects})
.then (result => {
this.allData = [
{
field: getFieldValue(),
componentType: getToRenderFieldType(),
isErrorFound : false,
}
]
this.showToast(SUCCESS_TITLE, FIELD_CREATION_SUCCESS_MESSAGE, SUCCESS);
this.isLoaded = true;
})
.catch (error => {
this.showToast(ERROR_TITLE, FIELD_CREATION_ERROR_MESSAGE, ERROR);
})
}
handleTypeChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.type = event.target.value;
this.handleFieldTypeChange(event.target.value, index);
}
handleTextLengthChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.textLength = event.target.value;
this.scaleMaxValue = 18 - event.target.value;
}
handlevisibleLinesChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.visibleLines = event.target.value;
}
handleScaleChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.scale = event.target.value;
}
handleTextAreaTypeChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.textAreaType = event.target.value;
}
handleCheckBoxDefaultChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.defaultvalue = event.target.value;
}
handleRelationshipLabelChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.relationshipLabel = event.target.value;
}
handleRelationshipNameChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.relationshipName = event.target.value;
}
handleReferenceToChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.referenceTo = event.target.value;
}
handleTypeChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.type = event.target.value;
this.handleFieldTypeChange(event.target.value, index);
}
handleTextLengthChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.textLength = event.target.value;
this.scaleMaxValue = 18 - event.target.value;
}
handlevisibleLinesChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.visibleLines = event.target.value;
}
handleScaleChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.scale = event.target.value;
}
handleTextAreaTypeChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.textAreaType = event.target.value;
}
handleCheckBoxDefaultChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.defaultvalue = event.target.value;
}
handleRelationshipLabelChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.relationshipLabel = event.target.value;
}
handleRelationshipNameChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.relationshipName = event.target.value;
}
handleReferenceToChange(event) {
const index = event.target.dataset.index;
this.allData[index].field.referenceTo = event.target.value;
}
handleSelectObjectChange(evert) {
this.selectObjects = evert.target.value;
if (this.selectObjects !== '') {
this.isObjectSelected = true;
}
this.referenceObjectList = this.SObjectList.filter(obj => obj.value !== this.selectObjects);
}
handleFieldTypeChange(value, index) {
this.allData[index].componentType = getToRenderFieldType();
if (value === "text") {
this.allData[index].componentType.isTextComponent = true;
}
else if (value === "number") {
this.allData[index].componentType.isNumberComponent = true;
}
else if (value === "checkbox") {
this.allData[index].componentType.isCheckboxComponent = true;
}
else if (value === "date") {
this.allData[index].componentType.isDateComponent = true;
}
else if (value === "lookup") {
this.allData[index].componentType.isLookupComponent = true;
}
else if (value === "longTextArea") {
this.allData[index].componentType.isLongTextAreaComponent = true;
}
else if (value === "masterdetail") {
this.allData[index].componentType.isMasterDetailComponent = true;
}
else if (value === "pickList") {
this.allData[index].componentType.isPickListComponent = true;
}
}
showToast(title, message, variant) {
this.dispatchEvent(
new ShowToastEvent({
title : title,
message : message,
variant : variant,
mode :'dismissable'
})
);
}
}
CreateCustomFields.html
<template>
<lightning-card title="Field Creator">
<div class="slds-m-around_large">
<div if:true={isLoaded}>
<div class="slds-p-horizontal_medium slds-p-vertical_medium">
<div class="slds-grid">
<div class="slds-col">
<lightning-combobox label="Select Object" value={selectObjects} options={SObjectList}
onchange= {handleSelectObjectChange} required={isRequired} ></lightning-combobox>
</div>
</div>
<template for:each={allData} for:item="data" for:index="index">
<div key={data.field.id}>
<template if:true={isObjectSelected}>
<hr style="margin-bottom: 2px"/>
<div class="slds-grid slds-gutters_small">
<div class="slds-col slds-side_6-of-12">
<h1>Field Number - {data.fieldNumber}</h1>
</div>
<div class="slds-col slds-side_6-of-12" style="text-align: right;">
<lightning-button label={label.REMOVE_THIS_FIELD} variant="distractive" data-index={index} onclick={handleDelete}></lightning-button>
</div>
</div>
<span if:true={data.fieldRemoveError} style="font-size: 10px;padding-left: 10px;color: red;">{data.fieldRemoveErrorMessage}</span>
<hr style="margin-top: 2px"/>
<div class="slds-grid slds-gutters_small">
<div class="slds-col slds-side_6-of-12">
<lightning-input label={label.FIELD_LABEL} value={data.field.label} onchange={handleLabelChange}
data-index={index} required={isRequired}></lightning-input>
<span if:true={data.isErrorFound} style="font-size: 10px;padding-left: 10px;color: red;">{errorMessage}</span>
</div>
<div class="slds-col slds-side_6-of-12">
<lightning-input label={label.FIELD_API_NAME} value={data.field.apiName}
onchange={handleAPINameChange} data-index={index} required={isRequired} readonly={isRequired}></lightning-input>
</div>
</div>
<div class="slds-grid">
<div class="slds-col slds-side_12-of-12">
<lightning-combobox label={label.FIELD_TYPE} value={data.field.type} options={fieldTypeOptions}
onchange={handleTypeChange} data-index={index} required={isRequired}></lightning-combobox>
</div>
</div>
</template>
<template if:true={data.componentType.isTextComponent}>
<lightning-input label={label.TEXT_LENGTH} type="number" value={data.field.textLength}
onchange={handleTextLengthChange} data-index={index} min = "1" max = "255" required={isRequired}></lightning-input>
</template>
<template if:true={data.componentType.isLongTextAreaComponent}>
<div class="slds-grid slds-gutters_small">
<div class="slds-col slds-side_6-of-12">
<lightning-input label={label.TEXT_LENGTH_LONGTEXT} type="number" value={data.field.textLength}
onchange={handleTextLengthChange} data-index={index} min = "255" max = "131072" required={isRequired}></lightning-input>
</div>
<div class="slds-col slds-side_6-of-12">
<lightning-input label={label.VISIBLE_LINES} type="number" value={data.field.visibleLines}
onchange={handlevisibleLinesChange} data-index={index} min = "2" max = "99" required={isRequired}></lightning-input>
<span if:true={data.isErrorFound} style="font-size: 10px;padding-left: 10px;color: red;">{errorMessage}</span>
</div>
</div>
</template>
<template if:true={data.componentType.isNumberComponent}>
<div class="slds-grid slds-gutters_small">
<div class="slds-col slds-side_6-of-12">
<lightning-input label={label.NUMBER_LENGTH} type="number" value={data.field.textLength}
onchange={handleTextLengthChange} data-index={index} min = "1" max = "18" required={isRequired}></lightning-input>
</div>
<div class="slds-col slds-side_6-of-12">
<lightning-input label={label.DECIMAL_PLACES} type="number" value={data.field.scale}
onchange={handleScaleChange} data-index={index} min = "0" max = {scaleMaxValue} required={isRequired}></lightning-input>
</div>
</div>
</template>
<template if:true={data.componentType.isCheckboxComponent}>
<lightning-combobox label={label.DEFAUL_VALUE} value={data.field.defaultvalue} options={checkBoxDefaulOption}
onchange={handleCheckBoxDefaultChange} data-index={index}></lightning-combobox>
</template>
<template if:true={data.componentType.isLookupComponent}>
<div class="slds-grid slds-gutters_small">
<div class="slds-col slds-side_6-of-12">
<lightning-input label={label.RELATIONSHIP_LABEL} value={data.field.relationshipLabel} onchange={handleRelationshipLabelChange}
data-index={index} required={isRequired}></lightning-input>
</div>
<div class="slds-col slds-side_6-of-12">
<lightning-input label={label.RELATIONSHIP_NAME} value={data.field.relationshipName}
onchange={handleRelationshipNameChange} data-index={index} required={isRequired}></lightning-input>
</div>
</div>
<div class="slds-grid">
<div class="slds-col slds-side_12-of-12">
<lightning-combobox label={label.RELETED_TO} value={data.field.referenceTo} options={referenceObjectList}
onchange={handleReferenceToChange} data-index={index} required={isRequired}></lightning-combobox>
</div>
</div>
</template>
<template if:true={data.componentType.isMasterDetailComponent}>
<div class="slds-grid slds-gutters_small">
<div class="slds-col slds-side_6-of-12">
<lightning-input label={label.RELATIONSHIP_LABEL} value={data.field.relationshipLabel} onchange={handleRelationshipLabelChange}
data-index={index} required={isRequired}></lightning-input>
</div>
<div class="slds-col slds-side_6-of-12">
<lightning-input label={label.RELATIONSHIP_NAME} value={data.field.relationshipName}
onchange={handleRelationshipNameChange} data-index={index} required={isRequired}></lightning-input>
</div>
</div>
<div class="slds-grid">
<div class="slds-col slds-side_12-of-12">
<lightning-combobox label={label.RELETED_TO} value={data.field.referenceTo} options={referenceObjectList}
onchange={handleReferenceToChange} data-index={index} required={isRequired}></lightning-combobox>
</div>
</div>
</template>
</div>
</template>
<div class="slds-p-top_small">
<lightning-button class= "slds-p-around_small" label={label.ADD_FIELD} variant="brand" onclick={addField}></lightning-button>
<lightning-button class= "slds-p-around_small" label={label.CREATE_FIELDS} variant="brand" onclick={createFields}></lightning-button>
</div>
</div>
</div>
<div if:false={isLoaded} class="slds-is-relative slds-align_absolute-center" style="height:100px;">
<lightning-spinner
alternative-text="Loading..." variant="brand">
</lightning-spinner>
</div>
</div>
</lightning-card>
</template>
CreateCustomFields.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>56.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
customFieldHelper.js
const getToRenderFieldType = () => {
return {
"isTextComponent" : false,
"isNumberComponent" : false,
"isCheckboxComponent" : false,
"isDateComponent" : false,
"isLookupComponent" : false,
"isLongTextAreaComponent" : false,
"isMasterDetailComponent" : false,
"isPickListComponent" : false,
}
}
const getFieldValue = () => {
return{
"id":0,
"label":"",
"apiName":"",
"type":"",
"textLength":"",
"defaultvalue":"",
"relationshipLabel" : "",
"relationshipName" : "",
"referenceTo" : "",
"visibleLines" : "",
"scale" : 0,
}
}
const getFieldTypeOption = () => {
return [
{ label: 'None', value: '' },
{ label: 'Text', value: 'text' },
{ label: 'Number', value: 'number' },
{ label: 'Checkbox', value: 'checkbox' },
{ label: 'Date', value: 'date' },
{ label: 'Lookup', value: 'lookup' },
{ label: 'Long Text Area', value: 'longTextArea' },
{ label: 'Master Detail', value: 'masterdetail' },
];
}
const getCheckBoxDefalutOptions = () => {
return [
{ label: 'None', value: '' },
{ label: 'True', value: 'true' },
{ label: 'False', value: 'false' },
]
}
const getFormatedAPIName = (inputString) => {
if(inputString !== ''){
inputString = inputString.trim();
let modifiedString = inputString.replace(/\s+/g, "_");
if(!modifiedString.endsWith("__c")){
modifiedString = modifiedString + "__c";
}
return modifiedString;
}
else {
return '';
}
}
const hasSpecialCharacters = (inputString) => {
// Regular expression to match special characters
var specialCharsRegex = /[^A-Za-z0-9\s]/;
// Check if the input string contains any special characters
return specialCharsRegex.test(inputString);
}
export {
getToRenderFieldType,
getFieldValue,
getFieldTypeOption,
getCheckBoxDefalutOptions,
getFormatedAPIName,
hasSpecialCharacters
};
2. CreateCustomObject
CreateCustomObject.js
import { LightningElement, track, api } from 'lwc';
import createObject from '@salesforce/apex/createCustomObjectController.createObject';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import CREATE_OBJECT from '@salesforce/label/c.CREATE_OBJECT';
import OBJECT_NAME from '@salesforce/label/c.OBJECT_NAME';
import OBJECT_API_NAME from '@salesforce/label/c.OBJECT_API_NAME';
import OBJECT_PLURAL_NAME from '@salesforce/label/c.OBJECT_PLURAL_NAME';
import ERROR_MESSAGE from '@salesforce/label/c.ERROR_MESSAGE';
import ERROR from '@salesforce/label/c.ERROR';
import SUCCESS from '@salesforce/label/c.SUCCESS';
import OBJECT_CREATION_SUCCESS_MESSAGE from '@salesforce/label/c.OBJECT_CREATION_SUCCESS_MESSAGE';
import OBJECT_CREATION_ERROR_MESSAGE from '@salesforce/label/c.OBJECT_CREATION_ERROR_MESSAGE';
import SUCCESS_TITLE from '@salesforce/label/c.SUCCESS_TITLE';
import ERROR_TITLE from '@salesforce/label/c.ERROR_TITLE';
export default class CreateCustomObject extends LightningElement {
isRequired = true;
@track objectName = '';
@track objectApiName = '';
@track objectPluralName = '';
errorFound = false;
errorMessage = '';
label = {
CREATE_OBJECT : CREATE_OBJECT,
OBJECT_NAME :OBJECT_NAME,
OBJECT_API_NAME : OBJECT_API_NAME,
OBJECT_PLURAL_NAME : OBJECT_PLURAL_NAME
}
isLoaded = true;
handleNameChange(event) {
if(this.hasSpecialCharacters(event.target.value)){
this.errorFound = true;
this.errorMessage = ERROR_MESSAGE;
}
else{
this.objectName = event.target.value;
this.objectApiName = this.getFormatedAPIName(this.objectName);
}
}
handleObjectPluralChange(event) {
this.objectPluralName = event.target.value;
}
handleAPIChange(event) {
this.objectApiName = this.getFormatedAPIName(event.target.value);
}
getFormatedAPIName(inputString) {
if(inputString !== ''){
inputString = inputString.trim();
let modifiedString = inputString.replace(/\s+/g, "_");
if(!modifiedString.endsWith("__c")){
modifiedString = modifiedString + "__c";
}
return modifiedString;
}
else {
return '';
}
}
hasSpecialCharacters(inputString) {
// Regular expression to match special characters
var specialCharsRegex = /[^A-Za-z0-9\s]/;
// Check if the input string contains any special characters
return specialCharsRegex.test(inputString);
}
handleCreateObject() {
this.isLoaded = false;
let data = {};
if (this.objectName) {
data.objectName = this.objectName;
}
if (this.objectApiName) {
data.objectAPI = this.objectApiName
}
if (this.objectPluralName) {
data.objectPlural = this.objectPluralName
}
createObject({
data: JSON.stringify(data)
})
.then( result => {
this.showToast(SUCCESS_TITLE, OBJECT_CREATION_SUCCESS_MESSAGE, SUCCESS);
this.objectName = '';
this.objectApiName = '';
this.objectPluralName = '';
this.isLoaded = true;
})
.catch (error => {
this.showToast(ERROR_TITLE, OBJECT_CREATION_ERROR_MESSAGE, ERROR);
})
}
showToast(title, message, variant) {
this.dispatchEvent(
new ShowToastEvent({
title : title,
message : message,
variant : variant,
mode :'dismissable'
})
);
}
createCustomObject.html
<template>
<div if:false={isLoaded} class="slds-is-relative slds-align_absolute-center" style="height:100px;">
<lightning-spinner
alternative-text="Loading..." variant="brand">
</lightning-spinner>
</div>
<div if:true={isLoaded}>
<lightning-card title="Create Custom Object">
<lightning-input label= {label.OBJECT_NAME} onchange={handleNameChange} value = {objectName} ></lightning-input>
<lightning-input label= {label.OBJECT_API_NAME} required={isRequired} readonly={isRequired} value = {objectApiName} ></lightning-input>
<lightning-input label= {label.OBJECT_PLURAL_NAME} required={isRequired} onchange={handleObjectPluralChange} ></lightning-input>
<br/>
<lightning-button class= "slds-p-around_small" label= {label.CREATE_OBJECT} variant="brand" onclick={handleCreateObject}></lightning-button>
</lightning-card>
</div>
</template>
createCustomObject.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>57.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
3. Object Manager
ObjectManager.Js
import { LightningElement } from 'lwc';
export default class ObjectManager extends LightningElement {
whatToDoOptions = [
{ label: 'None', value: '' },
{ label: 'Create Field On existing Object', value: 'createFieldsOnExistingObject' },
{ label: 'Create New Object', value: 'createNewObject' }
];
whatToDoValue = '';
isCreateFieldsOnExistingObject = false;
isCreateNewObjectWithFields = false;
handleChangeWhatToDo(event) {
this.whatToDoValue = event.target.value;
if (this.whatToDoValue == 'createFieldsOnExistingObject') {
this.isCreateFieldsOnExistingObject = true;
this.isCreateNewObjectWithFields = false;
}
else if (this.whatToDoValue == 'createNewObject') {
this.isCreateNewObjectWithFields = true;
this.isCreateFieldsOnExistingObject = false;
}
}
}
ObjectManager.html
<template>
<lightning-card title="Object Manager" >
<div class="slds-p-horizontal_medium slds-p-vertical_medium">
<lightning-combobox
label="Select What to Do"
value={whatToDoValue}
options={whatToDoOptions}
onchange={handleChangeWhatToDo}
></lightning-combobox>
</div>
<div if:true = {isCreateFieldsOnExistingObject} class="slds-p-horizontal_medium slds-p-vertical_medium">
<c-create-custom-fields></c-create-custom-fields>
</div>
<div if:true = {isCreateNewObjectWithFields} class="slds-p-horizontal_medium slds-p-vertical_medium">
<c-create-custom-object></c-create-custom-object>
</div>
</lightning-card>
</template>
ObjectManager.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>58.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
Now We are almost set. After creating all classes and the LWC component. We just need to edit any page and add the ObjectManager LWC component. These are the steps to be followed:-
Step 1:- Go to the page where you want to add the ObjectManager component. In my case, I am adding it to the home page.
Click setup
Edit page

Step 2:- Add the ObjectManager component to your page layout.

Step 3: - After adding the component, you will be able to see ObjectManager on your Page.
1. Select what to do
a) Here first I am choosing Create New Object

b) Fill in all the required fields and then click on the Create Object button.

c) Your new object will created successfully and you will get the success message. You can see your newly created object from the object manager.

2. If you choose Create fields on Existing Object, you will get this layout.
Here you need to choose the object on which you want to create fields.


Select the object where you want to create fields

Fill up all the required information. Then choose the field type and then click Create fields.

After clicking the Create button your fields will be created in the selected object.

In this way, you can create multiple fields at a time with different field types.
Happy Coding! You can leave a comment to help me understand how the blog helped you. If you need further assistance, please get in touch with us at Reach us. You can click "Reach Us" on the website and share the issue with me.
Blog Credit:
S. Chaudhary
Salesforce Developer
Avenoir Technologies Pvt. Ltd.
Reach us: team@avenoir.ai
Yorumlar