Admin Friendly Lightning Component

The advantage of using Salesforce CRM over any other CRM in the market is a strong foundation and easy to customize the application as per your business needs. With the correct set of skills, Salesforce Admin can perform a lot of customization to their org without necessarily having any coding knowledge.

Salesforce’s low code approach for many customization needs for their clients has been much appreciated by the business. And 1 of the main reason for the popularity in the CRM industry.

Customization or Development

When Salesforce Developers tend to jump on a custom solution for any business requirement, its best practice to think click before you code. Remember that every line code you are writing in the Salesforce, you will require to test and maintain that with Salesforce’s seasonal release. This release occurs three times (Spring, Winter & Fall) in a year. Not only that, any modification to that code might require a developer’s engagement.

It’s not always easy to engage a developer for small configuration modifications that you will need to make to your org which will impact the custom lightning components.

I am not by any means trying to scare you from developing a custom code in salesforce. Instead of asking to be considerate about Admins in your team when developing any custom code.

Page layouts are flexible but not always suitable for every business needs. In addition to that, you cannot use standard page layouts to all different locations within your salesforce app like the home page or utility bar.  

 A good example where you may need a custom lightning component page layout is for lightning flow. As part of lightning screen flow, if you wish to display a form, you will require to create many text box components. You will then need to map it back to the field for it to create or update records. After all that, you may not get the desired result because lightning flow doesn’t support more than 1 column on the screen. As you can imagine having only 1 row for fields will make form a very long and not idle experience for your end-user.

Challenges of using Screen component within Lightning Flow

There are many challenges other than less pleasant user experience with the screen component in lightning flow. List below contains small requirement changes yet it will require you to modify the screen component. Which is twice the amount of work because you will need to modify this on standard page layout as well as on screen component.

  • Business needs an extra required field on the page layout
  • A new section needs to be added to organize page layout better
  • Field’s label needs to be changed

Any developer, including myself, when presented with such a requirement, will jump to the conclusion quickly that this will need a custom lightning component.

Lightning Record Page which can be maintained by Awesome Admin within your team

However, What if I tell you that you can develop a custom lightning record page. Your administrator can maintain this page and modify it without the developer’s help. Imagine them not having to reach out to the developer to add that new field on the page layout? Or one more field on the page layout needs to be required, and admins can make that change themself on the page layout?

With the below lightning component code, it will automatically fetch all fields along with its section and label directly from the mentioned page layout. Your admin can easily make that modification on the page layout for any of the above business requirements.

The component code:

<aura:component implements="flexipage:availableForAllPageTypes" access="global" controller="DynamicPageLayout">

    <aura:attribute name="disabled" type="Boolean" default="false" />
    <aura:attribute name="layoutSections" type="List" />
    <aura:attribute name="saved" type="Boolean" default="false" />
    <aura:attribute name="showSpinner" type="Boolean" default="true" />
    <aura:attribute name="fieldName" type="String" default="StageName" />
    
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <lightning:card title="">
        <aura:if isTrue="{!v.showSpinner}">
            <lightning:spinner />
        </aura:if>
        <aura:if isTrue="{!!v.saved}">
            <lightning:recordEditForm
                onload="{!c.handleLoad}"
                onsubmit="{!c.handleSubmit}"
                onsuccess="{!c.handleSuccess}"
                objectApiName="Contact">
                <!-- the messages component is for error messages -->
                <lightning:messages />
                
                <aura:iteration items="{!v.layoutSections}" var="section">
                    <div class="slds-section slds-is-open">
                    	<h3 class="slds-section__title">
                            {!section.label}
                        </h3>
                        <div class="slds-section__content">
                            <lightning:layout multipleRows="{!section.totalColumns > 1 }">

                                <aura:iteration items="{!section.lstFields}" var="field">

                                    <lightning:layoutItem size="{! 12/section.totalColumns }" flexibility="auto" padding="around-small">
                                        <aura:if isTrue="{!(!field.isReadOnly)}">
                                            <lightning:inputField fieldName="{!field.fieldName}" />	
                                            <aura:set attribute="else">
                                                <lightning:outputField fieldName="{!field.fieldName}" />	 
                                            </aura:set>
                                        </aura:if>
                                    </lightning:layoutItem>

                                </aura:iteration>
                         </lightning:layout>
                        </div>
                    </div>
                </aura:iteration>
                <lightning:layout verticalAlign="center" class="x-large">
                	<lightning:layoutItem padding="around-large"> 
                        <div class="slds-m-top_medium">
                        	<lightning:button disabled="{!v.disabled}" variant="brand" type="submit" name="save" label="Save" />
                    	</div>
                    </lightning:layoutItem>   
            	</lightning:layout>
            </lightning:recordEditForm>
            <aura:set attribute="else">
                <p>Saved!</p>
            </aura:set>
        </aura:if>
    </lightning:card>
</aura:component>

The apex class:

public class DynamicPageLayout {
	
    @AuraEnabled 
    public static List<LayoutSection> getPageLayoutFields() {
        
        List<LayoutSection> lstSections = new List<LayoutSection>();
        
        try {
            
            List<String> componentNameList = new List<String>{'Contact-Contact Layout'};
           
            List<Metadata.Metadata> components = Metadata.Operations.retrieve(Metadata.MetadataType.Layout, componentNameList);
            Metadata.Layout contLayout = (Metadata.Layout) components.get(0);
            
           
            for( Metadata.LayoutSection ls : contLayout.layoutSections ) {
                
                LayoutSection section = new LayoutSection( ls.label, ls.layoutColumns.size() );
                      
                List<LayoutColumn> lstColumns = new List<LayoutColumn>();
                Integer maxFieldsInColumn = 0;
                for( Metadata.LayoutColumn lc : ls.layoutColumns ) {
                    
                    LayoutColumn column = new LayoutColumn();
                  
                    if( lc.layoutItems != null ) { 
                        
                        if( maxFieldsInColumn < lc.layoutItems.size() ) {
                            maxFieldsInColumn = lc.layoutItems.size();
                        }
                        for( Metadata.LayoutItem li : lc.layoutItems ) {
                            
                                
                            column.lstFields.add( new LayoutField( li ) );
                        }
                    }
                    
                    if( column.lstFields.size() > 0 ) {
                    	lstColumns.add( column );
                    }
                }
                
                
                if( maxFieldsInColumn > 0 ) {
                    for( Integer i = 0; i < maxFieldsInColumn; i++ ) {
                        for( Integer j = 0; j < lstColumns.size(); j++ ){
                            if( lstColumns[j].lstFields.size() > i ) {
                            	section.lstFields.add( lstColumns[j].lstFields[i] );    
                            }    
                            else {
                                section.lstFields.add( new LayoutField() );
                            }
                        }    
                    }    
                }
                
                lstSections.add( section );
            }
        }
        catch( Exception e ){
            System.assert(false, e.getLineNumber() + ' : ' + e.getMessage() );
        }
        return lstSections;
    }
    
    public class LayoutSection {   
	@AuraEnabled public String label;
    	@AuraEnabled public List<LayoutField> lstFields;
        @AuraEnabled public Integer totalColumns;
        public LayoutSection( String label, Integer totalColumns ) {
	    this.label = label;
            this.totalColumns = totalColumns;
            this.lstFields = new List<LayoutField>();
        }
    }
    
    private class LayoutColumn {
    	private List<LayoutField> lstFields;    
        public LayoutColumn() {
            this.lstFields = new List<LayoutField>();
        }
    }
    
    public class LayoutField {
        @AuraEnabled public String fieldName;
        @AuraEnabled public Boolean isRequired;
        @AuraEnabled public Boolean isReadOnly;
        
        
        public LayoutField() {}
        
        public LayoutField( Metadata.LayoutItem li ) {
        	string np = [SELECT NamespacePrefix FROM Organization].NamespacePrefix;
			if(li.field!=null && li.field.endsWith('__c'))
            {
               
                this.fieldName = np+'__'+li.field;
            }
            else
              this.fieldName = li.field;  
            
            if( li.behavior == Metadata.UiBehavior.Required ) {
                this.isRequired = true;
            }
            else if( li.behavior == Metadata.UiBehavior.ReadOnly ) {
                this.isReadOnly = true;
            }    
        }
    }
}

The controller code is as below:

({
    doInit: function( component, event, helper ) {
        var action = component.get("c.getPageLayoutFields");
		action.setCallback(this, function(response) {
        	var state = response.getState();
			if (state === "SUCCESS") {
                component.set("v.layoutSections", response.getReturnValue() );
                console.log( response.getReturnValue() );
            }
            else if (state === "INCOMPLETE") {
            }
            else if (state === "ERROR") {
                var errors = response.getError();
				console.log( errors );
            }
        });
        
        $A.enqueueAction(action);
    },
    
    
	handleSuccess : function(component, event, helper) {
        var toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams({
            "title": "Success!",
            "message": "Updated",
            "type": "success"
        });
        toastEvent.fire();
        
    },
    handleLoad : function(component, event, helper) {
    	component.set("v.showSpinner", false);   
    },
    handleSubmit : function(component, event, helper) {
       
    }
})

The component can be added on the home page as shown in below screenshot:

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top