5

I have a custom Lightning component which is a simple input form with the Contact fields(required). Obviously the controller class has an insert statement for the Contact sObject.

Is there a way I can create an input component that is not specific to any sObject, and have a Property setup(Design file in the Aura definition bundle) that says: <design:attribute name="objectAPIName" label="sObject"/> which I can assign any sObject I wanted, and the form should dynamically show the required fields specific to the provided sObject?

Controller to fetch all the fields(Dynamically):

@AUraEnabled
public static List<String> dynamicField(String ObjectName){

    String objName = ObjectName;
    Schema.SObjectType t = Schema.getGlobalDescribe().get(objName);
    SObject s = t.newSObject();

    DescribeSObjectResult describeResultPP = s.getSObjectType().getDescribe();
    List<String> fieldNamesPP = new List<String>( describeResultPP.fields.getMap().keySet() );

    System.debug('Field attributes: ' +fieldNamesPP);

    return fieldNamesPP;
}

How can I make use of these fields to populate on the input form? My concern is, I get all the fields of that specific sObject, how can I only display/show required fields to fill in? Also I am not even sure how I can display those on a Lightning.cmp file.

2
  • 3
    This is absolutely possible, but is a significant amount of work. I have done something similar but will need to see if I can get permission to share any source code for it since it was a part of an ISV project. If not I can at least share the overall architecture that was used to implement this in a lightning component.
    – dsharrison
    Commented Feb 7, 2018 at 19:18
  • @dsharrison, can you create a small demo and share the code, This looks interesting.
    – user51525
    Commented Feb 8, 2018 at 5:17

1 Answer 1

2
+25

Let me share an overall architecture that I used to accomplish a very similar problem. The first thing I did was to create an apex class FieldDetail to model a field describe with @AuraEnabled get methods for all relevant information like label, data type, etc. Next, an inputField component was created to consume that information and conditionally render a specific component using $A.createComponent() depending on the data type. Note that we used both lightning namespaced components as well as some components from the strike project. Once we had an input that could render itself for a FieldDetail, it was simply a task of adding a top level controller that would retrieve a list of FieldDetail objects and iterating over them to render a c:inputField component for each field. In your case that controller may have a method signature similar to:

@auraEnabled
public static List<FieldDetail> getRequiredFieldDetails(String sObjectName)

You could then pass the sObjectName parameter from either a design attribute or using one of the built in interfaces like force:hasSObjectName and use object and field describes to build your list of FieldDetail objects.

== Code Sample Update ==

The following class represents the details for a specific field and can be returned from an @AuraEnabled method to be consumed in Lightning.

/**
 *  SObjectFieldDetail object provides information of an SObject Field for
 *  thge lightning context.
 */
public with sharing class SObjectFieldDetail {

  private SObject record { get; set; }

  private Schema.SObjectField field { get; set; }

  /**
   * Constructor
   */
  public SObjectFieldDetail(SObject record, Schema.SObjectField field) {
    this.record = record;
    this.field = field;
  }

  @auraEnabled
  /**
   * @returns    String value of the DisplayType of the field
   */
  public String getType() {
    Schema.DisplayType dataType = field.getDescribe().getType();
    return String.valueOf(dataType);
  }

  @auraEnabled
  /**
   * @returns    Label of the field
   */
  public String getLabel() {
    return field.getDescribe().getLabel();
  }

  @auraEnabled
  /**
   * @returns    Returns true if the field is html formatted. (Rich Text)
   *             URL Fields return false
   */
  public Boolean getIsHtmlFormatted() {
    return field.getDescribe().isHtmlFormatted();
  }

  @auraEnabled
  /**
   * @returns    True, if current user has read access.
   */
  public Boolean getHasReadAccess() {
    return field.getDescribe().isAccessible();
  }

  @auraEnabled
  /**
   * @returns    True, if current user has write access.
   */
  public Boolean getHasEditAccess() {
    return field.getDescribe().isCreateable();
  }

  @auraEnabled
  /**
   * @returns    Returns field name
   */
  public String getFieldName() {
    return field.getDescribe().getName();
  }

  @auraEnabled
  /**
   * @returns    Returns local field name
   */
  public String getFieldLocalName() {
    return field.getDescribe().getLocalName();
  }
  @auraEnabled
  /**
   * @returns    If the field type is picklist, returns List of the label of all picklist options.
   *             Else, returns empty list.
   */
  public List<String> getAllPicklistOptions() {
    List<String> picklistOptions = new List<String>();
    if(getType() == 'PICKLIST'){
      List<Schema.PicklistEntry> picklistValues = field.getDescribe().getPicklistValues();
      for(Schema.PicklistEntry picklistEntry : picklistValues){
        picklistOptions.add(picklistEntry.getLabel());
      }
    }
    return picklistOptions;
  }

  @auraEnabled
  /**
   * @returns    If the field type is picklist, returns List of the label of active picklist options.
   *             Else, returns empty list.
   */
  public List<String> getPicklistOptions() {
    List<String> picklistOptions = new List<String>();
    if(getType() == 'PICKLIST'){
      List<Schema.PicklistEntry> picklistValues = field.getDescribe().getPicklistValues();
      for(Schema.PicklistEntry picklistEntry : picklistValues){
        if(picklistEntry.isActive()){
          picklistOptions.add(picklistEntry.getLabel());
        }
      }
    }
    return picklistOptions;
  }

  @auraEnabled
  /**
   * @returns    If the field type is picklist, returns List of the label of active picklist options.
   *             Else, returns empty list.
   */
  public String getDefaultPicklistOption() {
    if(getType() == 'PICKLIST'){
      return String.valueOf(field.getDescribe().getDefaultValue());
    }
    return null;
  }

  @auraEnabled
  /**
   * @returns    Current value of the field
   */
  public Object getCurrentValue() {
    if(getHasReadAccess()){
      return record.get(field);
    }
    else {
      return null;
    }
  }

  @auraEnabled
  /**
   * @returns    String value of the DisplayType of the field
   */
  public String getSObjectName() {
    return record.getSObjectType().getDescribe().getName();
  }

  @auraEnabled
  /**
   * @returns    String value of the DisplayType of the field
   */
  public String getSObjectLocalName() {
    return record.getSObjectType().getDescribe().getLocalName();
  }

  @auraEnabled
  /**
   * @returns    Current max length of field
   */
  public Integer getFieldLength() {
    return field.getDescribe().getLength();
  }

  @auraEnabled
  /**
   * @returns    Current max length of field
   */
  public Integer getFieldPrecision() {
    return field.getDescribe().getPrecision();
  }

  @auraEnabled
  /**
   * @returns    Current max number of digits of field
   */
  public Integer getFieldDigits() {
    return field.getDescribe().getDigits();
  }

  @auraEnabled
  /**
   * @returns    Current max number of digits of field right of decimal
   */
  public Integer getFieldScale() {
    return field.getDescribe().getScale();
  }

  @auraEnabled
  /**
   * @returns    Current max number of digits of field right of decimal
   */
  public Decimal getStep() {
    Decimal step = 1;

    for(Integer i = 0; i < getFieldScale(); i++){
      step = step / 10;
    }
    return step;
  }

  @auraEnabled
  /**
   * @returns    Current max number of digits of field right of decimal
   */
  public Decimal getMax() {
    Integer count = getFieldPrecision() - getFieldScale();
    String str = '';

    for(Integer i = 0; i < count; i++){
      str = str + '9';
    }
    if(getFieldScale() > 0){
      str = str + '.';
      for(Integer i = 0; i < getFieldScale(); i++){
        str = str + '9';
      }
    }
    if(str.length() > 0){
      return Decimal.valueOf(str);
    }
    else {
      return 0;
    }
  }

You can then construct instances of this class by passing in a record (this could be a new record like new Contact() and a list of fields. If you wanted all required fields, you use code similar to

List<Schema.SObjectField> allContactFields = Schema.Contact.sObjectType.getDescribe().fields.getMap().values();
List<Schema.SObjectField> requiredContactFields = new List<Schema.SObjectField>();

for(Schema.SObjectField field : allContactFields) {
    if(field.getDescribe().isCreateable() && !field.getDescribe().isNillable() && !field.getDescribe().isDefaultedOnCreate()) {
        requiredContactFields.add(field);
    }
}
6
  • I have implemented a method like you mentioned FieldDetail, which I have added in my question(dynamicField). It does return all the fields from whatever sObject we pass via Design attribute. But my main question is, how can I differentiate different sObject fields when I iterate fieldNamesPP in the Lightning component (.cmp) file? I have no clue to how to render those fields on the input form. Also, if I would only want fields that are Required, how to extract those from the list: fieldNamesPP. Let me know if this doesn't make sense.
    – SatyaV
    Commented Feb 8, 2018 at 1:15
  • Your method is returning a String which is the field name. The major difference is that in my example I am returning a FieldDetail which has all information from the DescribeFieldResult in it (such as whether or not the field is required).
    – dsharrison
    Commented Feb 8, 2018 at 1:24
  • Can you help me with how to retrieve the DataType, Required and Length of all the fields for a given sObject (via my code)?
    – SatyaV
    Commented Feb 8, 2018 at 1:54
  • @ArnoldJr. Yes I can, let me put together a gist.
    – dsharrison
    Commented Feb 8, 2018 at 18:15
  • @ArnoldJr. I added some sample code directly to my answer.
    – dsharrison
    Commented Feb 8, 2018 at 19:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .