In my previous post, We learned how to do the dynamic in Lightning Component and In this post, we will learn how to use Field Set in Lightning Component.

Working with Field Sets: – You can use dynamic bindings to display fieldsets on your Visualforce pages. A fieldset is a grouping of fields. For example, you could have a fieldset that contains fields describing a user’s first name, middle name, last name, and business title. If the page is added to a managed package, administrators can add, remove, or reorder fields in a field set to modify the fields presented on the Visualforce page without modifying any code. But in Lightning Component Field Sets are not Supported.

In this Example, we will use a Single object fieldsets and save/create the records into the System. For this example, we will create Opportunity Record using Fieldset.

Step1 – Create “FieldSetComponentController” apex class. Use below code for the same.

[codesyntax lang=”java” container=”div” doclinks=”1″]

/*
 * @Created Date:- 21th APRIL 2018
 * @Author : AMIT SINGH
 * @Description : Class used for the lightning component which illustrate how to use Field Set in 
 *              : Salesforce Lightning Component
 * @Company : sfdcpanther
 * @Name : FieldSetComponentController
 */ 
public class FieldSetComponentController {
    
    /*
     * @Created Date:- 21th APRIL 2018
     * @Author : AMIT SINGH
     * @Name : getsObjects
     * @ReturnType : List<String>
     * @param : none
     * @Description : List all the Objects that have atlead one fieldSet 
     */ 
    @AuraEnabled 
    public static List<String> getsObjects(){
        List<String> sObjectList = new List<String>();
        FOR(Schema.SObjectType sObj : Schema.getGlobalDescribe().values()){
            // Show only those objects which have atleast one Field Set
            Map<String, Schema.FieldSet> FieldSetMap = sObj.getDescribe().fieldSets.getMap();
            if(FieldSetMap!=null && FieldSetMap.keySet().size() > 0){
                sObjectList.add(sObj.getDescribe().getName() +'####'+sObj.getDescribe().getLabel());
            }
        }
        return sObjectList;
    }
    
    /*
     * @Created Date:- 21th APRIL 2018
     * @Author : AMIT SINGH
     * @Name : getFieldSet
     * @ReturnType : List<String>
     * @param : String sObjectName
     * @Description : List all the Fiels Sets of a particular Object 
     */ 
    @AuraEnabled 
    public static List<String> getFieldSet(String sObjectName){
        Schema.SObjectType sObj = Schema.getGlobalDescribe().get(sObjectName);
        List<String> fieldSetList = new List<String>();
        FOR(Schema.FieldSet FS : sObj.getDescribe().fieldSets.getMap().values()){
            fieldSetList.add(fs.getName() +'####'+fs.getLabel());
        }
        return fieldSetList;
    }
    
    /*
     * @Created Date:- 21th APRIL 2018
     * @Author : AMIT SINGH
     * @Name : getFieldSetMember
     * @ReturnType : String
     * @param : String objectName , String fieldSetName
     * @Description : List all the Fields that are added into the FielsSet
     */
    @AuraEnabled //@future
    public static String getFieldSetMember(String objectName , String fieldSetName){
        
        List<FieldSetMemberWrapperClass> wrapperList = new List<FieldSetMemberWrapperClass>();
        Schema.SObjectType sObj = Schema.getGlobalDescribe().get(objectName);
        
        for(Schema.FieldSetMember fieldMember : sObj.getDescribe().fieldSets.getMap().get(fieldSetName).getFields()){
            FieldSetMemberWrapperClass wrapper = new FieldSetMemberWrapperClass();
            wrapper.isDBRequired = fieldMember.getDbRequired();
            wrapper.isRequired = fieldMember.getRequired();
            wrapper.fieldType = String.valueOf(fieldMember.getType());
            wrapper.fieldLabel = fieldMember.getLabel();
            wrapper.fieldAPIName = fieldMember.getFieldPath();
            if(String.valueOf(fieldMember.getType()) == 'PICKLIST' || 
               				String.valueOf(fieldMember.getType()) == 'MULTIPICKLIST'){
                wrapper.pickListValues = sObj.getDescribe().fields.getMap().get(fieldMember.getFieldPath())
                    									   .getDescribe().getPicklistValues();
            }
            wrapperList.add(wrapper);
        }
        //System.debug('#### wrapperList '+wrapperList);
        
        return JSON.serialize(wrapperList);
    }
    
    /*
     * @Created Date:- 21th APRIL 2018
     * @Author : AMIT SINGH
     * @Name : doUpsertObjects
     * @ReturnType : String
     * @param : sObject objectData
     * @Description : Upsert the Data into the Database and return the upserted record.
     */
    @AuraEnabled
    public static String doUpsertObjects(sObject objectData){
        upsert objectData;
        return JSON.serialize(objectData);
    }
    
    /*
     * @Description : Wrapper class contains the information about all the fields of a FieldSet
     */ 
    public class FieldSetMemberWrapperClass{
        
        @AuraEnabled
        public Boolean isDBRequired 					 { get; set; }
        @AuraEnabled
        public Boolean isRequired 						 { get; set; }
        @AuraEnabled
        public String fieldType 						 { get; set; }
        @AuraEnabled
        public String fieldLabel 						 { get; set; }
        @AuraEnabled
        public String fieldAPIName 					     { get; set; }
        @AuraEnabled 
        public List<Schema.PicklistEntry> pickListValues { get; set; }
        
    }
}

[/codesyntax]

 

 

See the comments into the Class and above each method to get the clear idea that which method is being used for which functionality.

Step2 – Create FieldSetComponent. Use below code for the same

[codesyntax lang=”xml” container=”div” doclinks=”1″]

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" 
                access="global" 
                controller="FieldSetComponentController">
    
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <!-- define the attributes to be used in the component -->
    <aura:attribute name="theObject" type="Aura.Component[]" />
    <aura:attribute name="sObjectName" type="Opportunity" default="{'sobjectType' : 'Opportunity'}" /> 
    <aura:attribute name="sObjectList" type="String[]" />
    <aura:attribute name="fieldSetList" type="String[]" />
    
    <!-- the attribute where we will set all the component related to Field Set-->
    <aura:attribute name="theForm" type="Aura.Component[]" />

	<!-- put the spinner component -->
    <!-- <c:Spinner /> -->
	<!-- spinner component end -->
    
    <!-- page header -->
    <div class="slds-page-header" style="background-color: darkturquoise;" >
        <div class="slds-media">
            <div class="slds-media__figure">
                <span class="slds-icon_container" 
                      title="Using Fieldset in Lightning Dynamic for All Object">
                    <lightning:icon iconName="utility:automate" size="small" 
                                    alternativeText="Indicates FieldSet"/>
                </span>
            </div>
            <div class="slds-media__body">
                <h1 class="slds-page-header__title slds-truncate slds-align-middle" 
                    title="FieldSets in Lightning">FieldSets in Lightning</h1>
                <p class="slds-text-body_small slds-line-height_reset" style="color:black">
                     Amit Singh • SFDCPanther • 20/04/2018
                </p>
            </div>
        </div>
    </div>
    <!-- page header end -->
    
    <div class="slds-m-around_small slds-grid slds-gutters">
        <div class="slds-col" style="padding-left: 100px;">
            <lightning:select name="selectObject" aura:id="selectObject" label="Select an Object" 
                              onchange="{!c.doSelectChange}">
                <aura:iteration items="{!v.sObjectList}" var="obj" >
                	<option value="{!obj.value}">{!obj.label}</option>
                </aura:iteration>
            </lightning:select>
        </div>
        <div class="slds-col" style="padding-left: 100px;">
            <aura:if isTrue="{!v.fieldSetList.length>0}" >
                <lightning:select name="fieldSet" aura:id="fieldSet" label="Select a FieldSet" 
                                  onchange="{!c.doFieldSetChange}">
                    <aura:iteration items="{!v.fieldSetList}" var="fs" >
                        <option value="{!fs.value}">{!fs.label}</option>
                    </aura:iteration>
                </lightning:select>
            </aura:if>
        </div>
        <div class="slds-col">
            
        </div>
    </div>
   
     <div class="slds-grid slds-wrap" style="padding-left: 110px;">
      <!--
        <div class="slds-size_1-of-2">
            <div class="slds-m-around_x-small">
                <lightning:input type="text" label="Industry" class="slds-input" value="{!v.sObjectName.Industry}" />
            </div>
        </div> -->
        <aura:iteration items="{!v.theForm}" var="facet" >
            <div class="slds-size_1-of-2">
                <div class="slds-m-around_x-small">
                    {!facet}
                </div>
            </div>
        </aura:iteration>
    </div>
</aura:component>

[/codesyntax]

 

 

theObject attribute is used to store all the input fields that are added into the FieldSet and then show into the component as UI.

sObjectName attribute defines the name of the Object for which we are going to use the FieldSet.

Step3 – Create FieldSetComponentController.js, use below code

[codesyntax lang=”javascript” container=”div” doclinks=”1″]

({
    doInit : function(component, event, helper) {
        console.log(component.get('v.sectionObjects'))
        helper.onInit(component, event, helper);
    },
    doSelectChange : function(component, event, helper) {
        helper.onSelectChange(component, event, helper);
    },
    doFieldSetChange : function(component, event, helper) {
        helper.onFieldSetChange(component, event, helper);
    },
    handlePress : function(component, event, helper) {
        var objectName = component.get('v.sObjectName');
        alert(JSON.stringify(objectName));
        helper.onUpserObject(component, event, helper);
    },
})

[/codesyntax]

 

 

Step4 – Create FieldSetComponentHelper.js, use below code for the same-

[codesyntax lang=”javascript” container=”div” doclinks=”1″]

({
    /*
     * @Description: - Create a Map and put all the inputtype 
     * : - with attribute and then use this map while creating the fieldsets.
     * Available inputs for "lightning:input" are below
     * checkbox, date, datetime, email, file, password, search, tel, url, number, radio, and toggle
     */ 
    configMap: {
        "string": { 
            componentDef: "lightning:input", 
            attributes: { 
                "class" : "slds-input container",
            } 
        },
        "checkbox": { 
            componentDef: "lightning:input", 
            attributes: { 
                "class" : "slds-checkbox__label"
            } 
        },
        "button" : {
            componentDef : "lightning:button",
            attributes : {
                "variant" : "brand",
                "iconName" : "utility:automate",
                "label" : "Submit Form"
            }
        },
        "picklist" : {
            componentDef : "ui:inputSelect",
            attributes : {
                "class" : "slds-select slds-select_container container"
            }
        },
        "multipicklist" : {
            componentDef : "lightning:dualListbox",
            attributes : {
                "sourceLabel" : "Available Options",
                "selectedLabe" : "Selected Options",
                "readonly" : false
            }
        },
        "textarea" : {
            componentDef : "lightning:textarea",
            attributes : {
                "class" : "slds-input container"
            }
        },
    },
    onInit : function(component, event, helper){
        var getSobject = component.get('c.getsObjects');
        getSobject.setCallback(this, function(response){
            var state = response.getState();
            if(component.isValid() && (state === 'SUCCESS' || state === 'DRAFT')){
                var sObjectList = response.getReturnValue();
                var listOptions = [];
                listOptions.push({
                    label : '--Select One--',
                    value : ''
                });
                for(var i=0; i <sObjectList.length; i++){
                    listOptions.push({
                        label : sObjectList[i].split('####')[1],
                        value : sObjectList[i].split('####')[0]
                    });
                }
                component.set('v.sObjectList', listOptions);
            }else if(state==='INCOMPLETE'){
                console.log('User is Offline System does not support drafts '
                           + JSON.stringify(response.getError()));
            }else if(state ==='ERROR'){
                
            }else{
                
            }
        });
        getSobject.setStorable();
        $A.enqueueAction(getSobject);
    },
    onSelectChange : function(component, event, helper){
        
        var selectedObject = component.find('selectObject').get('v.value');
        var getFieldSet = component.get('c.getFieldSet');
        
        component.set("v.theForm", []);
        
        getFieldSet.setParams({
            "sObjectName" :  selectedObject 
        });
        getFieldSet.setCallback(this, function(response){
            var state = response.getState();
            if(component.isValid() && (state === 'SUCCESS' || state === 'DRAFT')){
                var fieldsSetList = response.getReturnValue();
                var listOptions = [];
                listOptions.push({
                    label : '--Select One--',
                    value : ''
                });
                for(var i=0; i < fieldsSetList.length; i++){
                    listOptions.push({
                        label : fieldsSetList[i].split('####')[1],
                        value : fieldsSetList[i].split('####')[0]
                    });
                }
                component.set('v.fieldSetList', listOptions);
            }else if(state==='INCOMPLETE'){
                console.log('User is Offline System does not support drafts '
                           + JSON.stringify(response.getError()));
            }else if(state ==='ERROR'){
                
            }else{
                
            }
        });
        getFieldSet.setStorable();
        $A.enqueueAction(getFieldSet);
    },
    onFieldSetChange : function(component, event, helper){
        var self = this;
        var selectedObject = component.find('selectObject').get('v.value');
        var selectedfieldSet = component.find('fieldSet').get('v.value');
        
        var FiledSetMember = component.get('c.getFieldSetMember');
        FiledSetMember.setParams({
            "objectName" : selectedObject,
            "fieldSetName" : selectedfieldSet
        });
        FiledSetMember.setCallback(this, function(response){
            var state = response.getState();
            if(component.isValid() && (state === 'SUCCESS' || state === 'DRAFT')){
                var fieldSetMember = JSON.parse(response.getReturnValue());
                self.createForm(component, event, helper, fieldSetMember);
            }else if(state==='INCOMPLETE'){
                console.log('User is Offline System does not support drafts '
                           + JSON.stringify(response.getError()));
            }else if(state ==='ERROR'){
                console.log(response.getError());
            }else{
                
            }
        });
        FiledSetMember.setStorable();
        $A.enqueueAction(FiledSetMember);
    },
    createForm : function(component, event, helper, fieldSetMember){
        // Create a map with availale inputs and according to this use the global map.
        var lightningInputMap = new Map();
        lightningInputMap.set('string','string');
        lightningInputMap.set('checkbox','checkbox');
        lightningInputMap.set('date','date');
        lightningInputMap.set('datetime','datetime');
        lightningInputMap.set('email','email');
        lightningInputMap.set('file','file');
        lightningInputMap.set('password','password');
        lightningInputMap.set('search','search');
        lightningInputMap.set('tel','tel');
        lightningInputMap.set('url','url');
        lightningInputMap.set('number','number');
        lightningInputMap.set('radio','radio');
        
        // list of components to create and put into the component body..
        var inputDesc = [];
        var config = null;
        
        /*
         * parse the FieldSet members and then create the members dynamically 
         * and put those components into the component.
         */ 
        for(var i=0; i < fieldSetMember.length; i++){
            var objectName = component.getReference("v.sObjectName");
            if(lightningInputMap.has(fieldSetMember[i].fieldType.toLowerCase())){
                config = JSON.parse(
                    JSON.stringify(this.configMap['string'])
                ); 
                if(config){
                    config.attributes.label = fieldSetMember[i].fieldLabel;
                    config.attributes.type = fieldSetMember[i].fieldType;
                    config.attributes.required = 
                        	fieldSetMember[i].isRequired || fieldSetMember[i].isDBRequired;
                    config.attributes.value = 
                        	component.getReference('v.sObjectName.' + fieldSetMember[i].fieldAPIName);
                    inputDesc.push([
                        config.componentDef,
                        config.attributes
                    ]);
                }
            }else{
                if(fieldSetMember[i].fieldType.toLowerCase() === 'integer'){
                    config = JSON.parse(
                        JSON.stringify(this.configMap['string'])
                    );
                    config.attributes.label = fieldSetMember[i].fieldLabel;
                    config.attributes.type = 'number';
                    config.attributes.required = 
                        	fieldSetMember[i].isRequired || fieldSetMember[i].isDBRequired;
                    config.attributes.value = 
                        	component.getReference('v.sObjectName.' + fieldSetMember[i].fieldAPIName);
                    inputDesc.push([
                        config.componentDef,
                        config.attributes
                    ]);
                }else if(fieldSetMember[i].fieldType.toLowerCase() === 'phone'){
                    config = JSON.parse(
                        JSON.stringify(this.configMap['string'])
                    );
                    config.attributes.label = fieldSetMember[i].fieldLabel;
                    config.attributes.type = 'tel';
                    config.attributes.required = 
                        	fieldSetMember[i].isRequired || fieldSetMember[i].isDBRequired;
                    config.attributes.value = 
                        	component.getReference('v.sObjectName.' + fieldSetMember[i].fieldAPIName);
                    
                    inputDesc.push([
                        config.componentDef,
                        config.attributes
                    ]);
                }else if(fieldSetMember[i].fieldType.toLowerCase() === 'textarea'){
                    config = JSON.parse(
                        JSON.stringify(this.configMap['textarea'])
                    );
                    config.attributes.label = fieldSetMember[i].fieldLabel;
                    config.attributes.name = fieldSetMember[i].fieldLabel;
                    
                    config.attributes.required = 
                        	fieldSetMember[i].isRequired || fieldSetMember[i].isDBRequired;
                    config.attributes.value = 
                        	component.getReference('v.sObjectName.' + fieldSetMember[i].fieldAPIName);
                    
                    inputDesc.push([
                        config.componentDef,
                        config.attributes
                    ]);
                }else if(fieldSetMember[i].fieldType.toLowerCase() === 'picklist'){
                    config = JSON.parse(
                        JSON.stringify(this.configMap['picklist'])
                    );
                    config.attributes.label = fieldSetMember[i].fieldLabel;
                    config.attributes.name = fieldSetMember[i].fieldLabel;
                    var pickList = fieldSetMember[i].pickListValues;
                    var options = [];
                    for(var k=0; k<pickList.length; k++){
                        if(pickList[k].active){
                            options.push({
                                value : pickList[k].value,
                                label : pickList[k].label
                            });
                        }
                    }
                    config.attributes.options = options;
                    config.attributes.required = 
                        	fieldSetMember[i].isRequired || fieldSetMember[i].isDBRequired;
                    config.attributes.value = 
                        	component.getReference('v.sObjectName.' + fieldSetMember[i].fieldAPIName);
                    
                    inputDesc.push([
                        config.componentDef,
                        config.attributes
                    ]);
                }else if(fieldSetMember[i].fieldType.toLowerCase() === 'multipicklist'){
                    config = JSON.parse(
                        JSON.stringify(this.configMap['multipicklist'])
                    );
                    config.attributes.label = fieldSetMember[i].fieldLabel;
                    config.attributes.name = fieldSetMember[i].fieldLabel;
                    var pickList = fieldSetMember[i].pickListValues;
                    var options = [];
                    for(var k=0; k<pickList.length; k++){
                        if(pickList[k].active){
                            options.push({
                                value : pickList[k].value,
                                label : pickList[k].label
                            });
                        }
                    }
                    config.attributes.options = options;
                    config.attributes.required = 
                        	fieldSetMember[i].isRequired || fieldSetMember[i].isDBRequired;
                    config.attributes.value = 
                        	component.getReference('v.sObjectName.' + fieldSetMember[i].fieldAPIName);
                    /*
                    inputDesc.push([
                        config.componentDef,
                        config.attributes
                    ]);*/
                }
            }
        }
        var newConfig = JSON.parse(
            JSON.stringify(this.configMap['button'])
        );
        newConfig.attributes.onclick = component.getReference("c.handlePress");
        
        inputDesc.push([
            newConfig.componentDef,
            newConfig.attributes
        ]);
        
        $A.createComponents(inputDesc,
                            function(components, status, errorMessage){
                                if (status === "SUCCESS") {
                                    var form = [];
                                    for(var j=0; j < components.length; j++){
                                        form.push(components[j]);
                                    }
                                    component.set("v.theForm", form);
                                }else if (status === "INCOMPLETE") {
                                    console.log("No response from server or client is offline.");
                                }else if (status === "ERROR") {
                                    console.log("Error: " + errorMessage);
                                    console.log(errorMessage);
                                }
                            }
          );
    },
    onUpserObject : function(component, event, helper){
        var upsertObject = component.get('c.doUpsertObjects');
        upsertObject.setParams({
            "objectData" :  JSON.stringify(component.get('v.sObjectName'))
        });
        upsertObject.setCallback(this, function(response){
            var state = response.getState();
            if(component.isValid() && (state === 'SUCCESS' || state === 'DRAFT')){
                var upsertedRecord = JSON.parse(response.getReturnValue());
            }else if(state==='INCOMPLETE'){
                console.log('User is Offline System does not support drafts '
                           + JSON.stringify(response.getError()));
            }else if(state ==='ERROR'){
                console.log(response.getError());
            }else{
                console.log('Unknown Error While making DML'
                           + JSON.stringify(response.getError()));
            }
        });
        $A.enqueueAction(upsertObject);
    }
})

[/codesyntax]

 

The above helper javascript file is responsible for creating the dynamic component of the fields that are included into the fieldset. 

Step5 – Create FieldSetComponent.css, use code for the custom CSS that is being used for creating the layout.

[codesyntax lang=”css” container=”div” doclinks=”1″]

.THIS.container {
    width : 611px;
}
.THIS .slds-form-element{
    width : 470px;
    height: 95px;
}

.THIS .uiInputSelect{
        width: 468px;
}

.THIS .slds-textarea{
    height:62px;
}

[/codesyntax]

 

 

Step5 – Create FieldSetApplication.app, and put the FieldSetComponent inside the app to see the output of the component that we have created.

[codesyntax lang=”xml” container=”div” doclinks=”1″]

<aura:application access="Global" extends="force:slds" >
    <c:FieldSetComponent />
</aura:application>

[/codesyntax]

 

Key Points to remember: – 

  1. The above component works for single Object as of now means you need to create an attribute in the Component for which object you want to use the FieldSet. Like Account, Opportunity, Contact and etc. <aura:attribute name=”sObjectName” type=”Opportunity” default=”{‘sobjectType’ : ‘Opportunity‘}” />
  2. This does not support Multiple picklist fields.
  3. Does not support the lookup fields.

Output: –

 

I will post the new version of this blog with the lookup, dual and dynamic object support.

I will post the video link to this tutorial ASAP.

 

Happy learning 🙂 keep sharing 🙂

If you have any doubt then please come up in the comment section with OR you can tweet me here.

 

Our Score
Click to rate this post!
[Total: 0 Average: 0]

55 thoughts on “How to use Field Set in Lightning Component”

  1. hi bro,

    this code is not working for opportunity record creation.
    i got the error below

    This page has an error.You might just need to refresh it
    Access Check Failed’ Component.setupAttributesO:’name’ of component
    ‘markup://ui:inputselect {1:25;a}’ is not visible to ‘markup://c:FieldSetComponent {3 o}’
    Failing descriptor: k:FieldSetComponent}

    thanks,
    prakash

  2. Hi ,
    I want to use 4 to 5 Fieldsets on the component and all needs to be saved at a time (not Individually) Can you please suggest a way to o achieve that…
    Thanks in Advance

    Thanks
    Sarath M

    1. Yes, We can definitely do that. For that you need to some tweaks into the code.

      1 – You need to change the Field Set to Dual List.
      2 – Then in the Apex code you need to fetch the field related to the Selected FieldSet.

      Hope this helps 🙂

      Regards,
      Amit

    1. Hi Sarath,

      Is there any specific need to use Different Child Components instead of using the Single Child Component and use single fieldset?

  3. Hi Amit,

    Thanks for the wonderful Functionality.
    Im getting following Error when im trying to select the fieldset for the 2nd time from other object.
    Step 1 – Account –> Fieldset 1 –> Account Form1 (Working fine)
    Step 2 – Account –> Fieldset 2 –> Account Form2 (Working fine)
    Step 3 – Opportunity –> Fieldset 1 –> Opportunity Form (sometime working, most of the time error)
    Step 3 – Opportunity –> Fieldset 1 –> Opportunity Form (Error)

    Error Msg –
    This page has an error. You might just need to refresh it.
    Action failed: ui:inputSelect$controller$valueChange [Maximum call stack size exceeded]
    Callback failed: apex://testAppMaqsood.FieldSetComponentController/ACTION$getFieldSetMember
    Failing descriptor: {ui:inputSelect$controller$valueChange

    Regards,
    Maq

    1. Hi Maq,

      I have tested the same at my side and everything is working fine. I followed your test steps as well. the only suspicious item I can think that is In the Component you need to change the default value of the object as this is not the dynamic for all the Objects.
      <aura:attribute name=”sObjectName” type=”Opportunity” default=”{‘sobjectType’ : ‘Opportunity’}” />

      Also, when you are getting the Error after submitting the Error?

      Regards,
      SFDCPanther

      1. Hi Amit,

        No, its working fine as soon as i check the option.i don’t have to reload the page.I think, it has to do with memory of the transition.it work perfectly fine when i tried changing the fieldset option from the same object(ex – Account). it throwing the error when im changing the object and select the fieldset.
        Anyways, thanks for the response.
        I implemented similar functionality (dynamically showing field from objects based on selection ) in visualforce page like 3 years back. Nice to see that in lightning.

        Regards,
        Maq

  4. Hi Amit, I just figured out how to dynamically use field sets in lightning using generic code and …recordForm.
    http://lightninghack.com/generic-fieldsets-lightning/ . It allows Admin to name the object and fieldset from App Builder, so it works for pretty much any object. *****It uses your apex for getFieldset members ! Thank you again for your great posts and code! – Gus
    -ps I tried to give you a shout out on the success community but it may not have reached you. I see your success profile now and will get it right next time!!!

  5. Hi Amith , I have Copied entire code (as it is) to achieve same but i could n’t get field set members inside the field set in Component output.

  6. Please resolve this error:
    Access Check Failed! AttributeSet.get(): attribute ‘sectionObjects’ of component ‘markup://c:FieldSetComponent {4203:0}’ is not visible to ‘markup://c:FieldSetComponent {4203:0}’.

  7. HI Amit,

    Is your Field set supports Lookups and Multiselect picklist…. Please let me know.

    Regards,
    Learner

  8. Hello sir i have copied the entire code to check it’s functionality but i am not able to get desired output as shown in below tutorial video.I can’t find any option to select an object in aura.

    1. Hi Vicky,

      Create a FieldSet in any Object Like Account, Contact, Opportunity OR Any custom Object. Once you will create a fieldset the object will appear to select. As there is a filter to show only those objects which have fieldset.

      Let me know if you need anything else.
      Thanks,
      SFDCPanther

    2. This might help. Check you API version.

      “Prior to API version 44.0, to cache data returned from an Apex method, you had to call setStorable() in JavaScript code on every action that called the Apex method. For API version of 44.0 or higher, you must mark the Apex method as storable (cacheable) and you can get rid of any setStorable() calls in JavaScript code. The Apex annotation approach is better because it centralizes your caching notation for a method in the Apex class.”

      You will see the related error “Apex methods that are to be cached must be marked as @AuraEnabled(cacheable=true)” if you add the console.log() on line 71 in helper.js

      1. hi david i’m experiencing this on api 50 but sfdc doesnt let me change it back to a version prior to 46. i added the (cacheable=true) to all the apex methods but still getting that cache error. any ideas?

        1. The below code is working for me.

          @AuraEnabled
          public static List < String > getFieldSet(String sObjectName) {
          Schema.SObjectType sObj = Schema.getGlobalDescribe().get(sObjectName);
          Schema.DescribeSObjectResult d = sObj.getDescribe();
          Map < String, Schema.FieldSet > FsMap = d.fieldSets.getMap();
          List < String > fieldSetList = new List < String > ();
          FOR(Schema.FieldSet FS: FsMap.values()) {
          fieldSetList.add(fs.getName() + '####' + fs.getLabel());
          }
          return fieldSetList;
          }

  9. Hi Amit,
    Your given code is working fine for me.
    But when i am overriding standard new button with FieldSetcomponent.cmp it is throwing me a following error.

    Uncaught Action failed: ui:inputSelect$controller$valueChange [Maximum call stack size exceeded]
    Callback failed: apex://FieldSetComponentController/ACTION$getFieldSetMember

    Do you have any idea about this, your help would be appreciated.

    Thanks & Regards,
    Nilesh

  10. Hi Amit,
    I have used your fieldset logic for one object and it is working fine.
    but when i override standard New button with fieldsetcomponent.cmp it is throwing a following error.
    Error:——-
    Uncaught Action failed: ui:inputSelect$controller$valueChange [Maximum call stack size exceeded]
    Callback failed: apex://FieldSetComponentController/ACTION$getFieldSetMember

    Do you have any idea about this? Is it possible to override standard ‘new’ button with fieldsetcomponent?

    Your feedback would be appreciated.

    Thanks,
    Nilesh

    1. Hi Nitish,

      Yes. You can do that. Have you defined the Default FieldSet in the Component for that Object?
      Also, I believe that you have modified the ApexClass to get the FieldSet for the Object.

      Thanks & Regards,
      SFDCPanther

      1. Hi Amit,
        Thanks for your response.
        Yes, i have modified the ApexClass to get the fieldset for one particular object and it works perfect when component is previewed in developer console but error occurred when i click on the ‘New’ standard button for which the component is overrided.
        I have only modified the ApexClass ‘getFieldSetMember()’ method.
        I have sent you a code on your gmail Id, please review it once you get a time.

        Thanks,
        Nilesh

          1. Hi Amit. I am stuck with this problem and unable to resolve it. Can you please help with it? I have also emailed sample code.

  11. How this would be different when implementing the same with lwc, do we need to still have the wrapper class?

    1. Hi,

      Dynamic component creation does not support in LWC. for this, you need to use the concept of render to dynamically create the input fields in LWC.

      You can use the same Wrapper class in LWC as well. This may take time but you can do that it is doable.

      Thanks,
      SFDCPanther

  12. Hi i am using same code, but not able to get list of object on which I have created fieldset. It is showing empty drop down list

  13. Hi i am using same code, but not able to get list of object on which I have created fieldset. It is showing empty drop down list. No error coming.

  14. did this break with a later API? i made a field set on opportunity and also on my customer object. yet when i run this app no objects appear in the dropdown? no errors either. when i put a line to debug output right after the getObjects apex function it doesnt even get to this line?

    system.debug(‘we are at the getsObjects method!’);

    1. The below code is working for me.

      @AuraEnabled
      public static List < String > getFieldSet(String sObjectName) {
      Schema.SObjectType sObj = Schema.getGlobalDescribe().get(sObjectName);
      Schema.DescribeSObjectResult d = sObj.getDescribe();
      Map < String, Schema.FieldSet > FsMap = d.fieldSets.getMap();
      List < String > fieldSetList = new List < String > ();
      FOR(Schema.FieldSet FS: FsMap.values()) {
      fieldSetList.add(fs.getName() + '####' + fs.getLabel());
      }
      return fieldSetList;
      }

      1. if you upgrade your API to v50 you’ll experience the error. a previous user posted about the cacheable tag on the methods. by adding that to several methods and then commenting out the setStorable it works. just more of an FYI for those that build the components where the API defaults to the latest and we cannot go back to v44.

        1. The code that I have shared with you in my previous comment is working on the Latest Salesforce API. I do not know what is the issue with you. BTW it’s good to know that you got it working.

  15. Checkboxes don’t render if they are added to the field set. i noticed on the FieldSetHelper.js there is code starting around line 150 that calls out specific types like string, integer, tel, etc. but there isnt a block of code to setup the checkboxes? having trouble determining the structure that should be used for checkboxes in this case. any ideas?

    1. You have to add the code for the checkboxes as the code is not supporting lookup and few other inputs. First, you need to check the checkbox input syntax and then based on that prepare the input to render

      1. ugh this will take me days and days to figure out the syntax. its the last piece i need to make this work.

        1. Add the below code inside createForm inside for Loop

          else if(fieldSetMember[i].fieldType.toLowerCase() === 'boolean'){
          config = JSON.parse(
          JSON.stringify(this.configMap['boolean'])
          );
          config.attributes.label = fieldSetMember[i].fieldLabel;
          config.attributes.type = 'checkbox';
          config.attributes.required =
          fieldSetMember[i].isRequired || fieldSetMember[i].isDBRequired;
          config.attributes.value =
          component.getReference('v.sObjectName.' + fieldSetMember[i].fieldAPIName);

                          inputDesc.push([
                              config.componentDef,
                              config.attributes
                          ]);
                      }
          

          1. awesome thank you. i was stuck thinking it should be a checkbox but is defined in SFDC actually as a boolean. do you know if there is a reason the elements become so spaced out? maybe just adding some slds additional classes should allow it to be a more condensed form.

            do you forsee any issues with also preloading data on a record load? e.g. the page loads for a contact with these fields using the appropriate field sets and then i need to populate what has already been saved for the record if it has already been entered.

          2. For the spacing, you might need to check the class related to the attribute and yes you can pre-load the data but that will be only for the selected record. It might take some effort.

  16. ok after some effort i was able to get the data to save and load for a selected record! however bumped into an issue:

    here i see you are setting the config.attributes.value to the component.getReference in the createForm helper method. however i want to set the value to the value from the DB. it only works if i add my for loop AFTER the config.attributes.value. it basically loops over all the fields and if the api name is a match it should use the value from a map i have stored in the aura:attribute. however once i do that and click the submit form save button it doesnt recognize that item in the form list to pass for the update. i’m confused as to why you are setting the config.attributes.value and how could i also set the real value of the inputs?

    config.attributes.value =
    component.getReference('v.sObjectName.' + fieldSetMember[i].fieldAPIName);

    //if there is a result matching the api name then push the data already saved
    for ( var key in studyParticipantDetails ) {
    //console.log('does ' + key + ' = ' + fieldSetMember[i].fieldAPIName + '?');
    if (key == fieldSetMember[i].fieldAPIName){
    config.attributes.value = studyParticipantDetails[key];
    }
    }

    1. Hi Matt,

      To set the value for any field you must need to use component.getReference. So could you please try

      component.getReference('v.sObjectName.' + fieldSetMember[i].fieldAPIName) = studyParticipantDetails[key];

      under if (key == fieldSetMember[i].fieldAPIName){ code.

      Let me see if this works.

      1. Getting a Field Integrity error when I try to save with that syntax:

        Failed to save FieldSetComponentHelper.js: ESLINT_ERROR: {c:FieldSetComponent – HELPER} line:col [303:29] –> Parsing error: Assigning to rvalue : Source

        1. i believe you can try with your existing code if you just put a line in something like the textarea and try to set the value to “foo” upon creation. and if you can load and save…

          1. UPDATE. i was able to get it working. the trick here was to not do it on the component creation logic… instead I put the API field name and the value in a map… then on the after result when the components are actually created loop through the map and set the values of the component that way! so in the results of $A.createComponents i added this for anyone that may need to do something similar:

            //now that the components are created we need to default the values to already selected fields from the map
            var existingValuesMap = component.get(‘v.mapValues’);

            if(existingValuesMap != null){
            for (var key in existingValuesMap){
            console.log('key = ' + key + ' and value = ' + existingValuesMap[key]['value']);
            var keyName = 'v.sObjectName.' + existingValuesMap[key]['key'];
            var keyValue = existingValuesMap[key]['value'];
            console.log('keyName = ' + keyName + ' and keyValue = ' + keyValue);
            component.set(keyName, keyValue);
            }
            }

Leave a Reply