6

In a custom lightning component where one is using the recordEditForm tag, how can one validate fields that are filled by the user?

For example, in a quick action to create a Case record, we can have the following line inside the recordEditForm:

<lightning:inputField aura:id="caseAccount" fieldName="AccountId"/>

How can one enable or disable a button if certain fields are assigned by the user? Like if the account is specified, then a button on the component is enabled for the user.

Making the field required is not an option.

2
  • just to clarify, you are looking for 2 things here - #1. to validate the fields viz., required, or any other validation on the form and #2. dynamically enable/disable a button based on a particular input value. Is this the correct understanding?
    – Jayant Das
    Commented Apr 4, 2018 at 20:56
  • I need #1 to do #2. Commented Apr 5, 2018 at 12:56

4 Answers 4

8

Here's a way that you can achieve this. Let's say I have the component as below. There's a field for Title, which is hidden when the component is rendered (and your use case is to enable that). This approach also makes sure that all your standard validations are taken care of, viz., required fields, email formatting (in the example below).

Component:

<lightning:recordEditForm aura:id="editForm" recordId="xxxx" objectApiName="Contact" >
    <lightning:inputField aura:id="firstNameField" fieldName="FirstName" />
    <lightning:inputField fieldName="LastName" />
    <lightning:inputField fieldName="Email" />
    <lightning:inputField aura:id="titleField" fieldName="Title" class="slds-hidden" />
    <lightning:button class="slds-m-top_small" variant="brand" name="update" label="Update" onclick="{!c.onSubmit}"/>
</lightning:recordEditForm>

Controller JS:

onSubmit : function(component, event, helper) {
    var firstName = component.find("firstNameField").get("v.value");


    // perform validations as required based on the value retrieved
    if(firstName === "xyz") {....}
    var titleField = component.find("titleField");
    $A.util.addClass(titleField, 'slds-visible');
    // based on the validations, invoke the below to submit the form
    //component.find("editForm").submit();
},

Few things to take care of on the component/JS are as below:

  1. Assign an id to the lightning:recordEditForm
  2. Associate a onclick event and function to the button used to save the form (if you are using a lighting:button as with type="submit" to submit the form, you may like to remove that)
  3. Perform any validations that you need in the controller function associated to the button viz., showing/hiding the element and submitting the form by getting the aura:id associated to recordEditForm and invoking a submit() (you can use submit() call if in edit mode) or call an apex aura method for any additional server side handling
  4. If the form is utilized for "edit" operations, provide a recordId to associate a record in lightning:recordEditForm. If the form is utilized for "create" operations, do not provide any recordId attribute, and you will get a new form to submit the input.
  5. To retrieve value entered on any input field, associate an aura:id and retrieve it's value in the JS as in the example
6
  • 1
    1. How can I assign an Id, if the record is being created with this form? (it does not exist in the database, so there's no Id). 2. How can I read what the user selected in the inputField? Your example does not cover this. Commented Apr 5, 2018 at 19:32
  • 1
    I assumed the usage of "lightning:recordEditForm" component in edit mode and the example is based on that assumption. If that's not the case, in case of new record, here's what I see from documentation for lightning:inputField: To create a record create layout, use this component with lightning:recordEditForm and pass in the object API name of the record you're creating. This may be the solution for new record creation scenario.
    – Jayant Das
    Commented Apr 5, 2018 at 19:41
  • And I verified it by removing the recordId attribute and it works fine. I will update the original answer to address both create vs. edit form implementation. Hope this helps you now.
    – Jayant Das
    Commented Apr 5, 2018 at 19:48
  • In your JS how can I "perform validations as required", if I cannot retrieve the fields values? Commented Apr 5, 2018 at 19:51
  • 1
    In my example above, you can easily retrieve the value from say the title field using: component.find("titleField").get("v.value"); For whichever component you need to retrieve the value from, just add an aura:id to that inputField and that should work. So let's say I need to retrieve the value from firstname field, in that case, I will associate an aura:id to that and use it in the JS. Updated that in the example in the answer. Hope it helps now to address all scenarios.
    – Jayant Das
    Commented Apr 5, 2018 at 19:56
1

A simple approach to validate field validity on lightning:inputField elements is by using built-in Lightning Component methods (outlined below). If you need certain behaviors based on specific inputs, the answers above do a good job of going through that.

Component:

<lightning:recordEditForm
                          aura:id="form"
                          objectApiName="Account">
    <lightning:inputField
                          aura:id="field"
                          required="true"
                          fieldName="Name"/>
</lightning:recordEditForm>
<lightning:button label="Save" onclick="{!c.handleSave}"/>

Controller:

handleSave : function(component, event, helper) {
    // Make sure fields are in an iterable object by casting to array if not already
    let fields = component.find("field");
    if (fields) {
        fields = Array.isArray(fields) ? fields : [fields];
    }
    else {
        fields = [];
    }
    const valid = fields.reduce((validSoFar, field) => {
        // Return whether all fields up to this point are valid and whether current field is valid
        // reportValidity returns validity and also displays/clear message on element based on validity
        return (validSoFar && field.reportValidity());
    }, true);
    // If all fields aren't validated, throw error message
    if (!valid) {
        const toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams({
            "message": "Review all error messages below to correct your data.",
            "type": "error"
        });
        toastEvent.fire();
    }
    // Otherwise submit save
    else {
        component.find("form").submit();
    }
}
0

Here's an approach I've devised a while ago:

 <aura:attribute name="recordUiCache" type="Object" access="private" />
 <lightning:recordEditForm
        density="comfy"
        onsuccess="{!c.redirect}"
        onsubmit="{!c.showSpinner}"
        onload="{!c.hideSpinner}"
        onerror="{!c.hideSpinner}"
        recordId="{!v.record.Id}"
        recordTypeId="{!v.record.RecordTypeId}"
        objectApiName="_Info__c">
    <lightning:card title="Some Details">
        <aura:if isTrue="{!v.showSpinner}">
            <lightning:spinner/>
        </aura:if>
        <aura:set attribute="actions">
            <lightning:button
                    variant="brand"
                    type="submit"
                    name="save"
                    label="Save"/>
        </aura:set>
        <lightning:messages/>
        <lightning:layout>
            <lightning:layoutItem padding="around-small">
                <lightning:inputField
                        class="customRequired"
                        aura:id="requiredField"
                        fieldName="SomeQuestion1__c"/>
                <lightning:inputField
                        fieldName="SomeOtherQuestion__c"/>
            </lightning:layoutItem>
        </lightning:layout>
    </lightning:card>
</lightning:recordEditForm>

CSS:

.THIS .customRequired {
    font-weight: 400;
}

.THIS .customRequired:before {
    content: "*";
    color: rgb(194, 57, 52);
    left: -0.5rem;
    position: absolute;
    top: 3px;
}

Controller:

hideSpinner: function (component, event, helper) {
    component.set('v.recordUiCache', event.getParam('recordUi'));
    component.set("v.showSpinner", false);
},
showSpinner: function (component, event, helper) {
    var invalidFields = helper.getFieldLabelsByApiName(
        component.get('v.recordUiCache'),
        helper.getBlankFields(component.find('requiredField')));
    if (invalidFields && invalidFields.length > 0) {
        helper.reportUserError(
            'Please complete all required fields:',
            invalidFields.join(', ')
        );
        event.preventDefault();
        return;
    }
    component.set("v.showSpinner", true);
}

Helper:

getFieldLabelsByApiName: function (recordUi, invalidFields) {
    if (recordUi && recordUi.objectInfo && recordUi.objectInfo.fields) {
        invalidFields = invalidFields.map(function (value) {
            var f = recordUi.objectInfo.fields[value];
            if (f) {
                return f.label;
            } else {
                return value;
            }
        })
    }
    return invalidFields;
},
getBlankFields: function (fields) {
    if(!fields) {
        return [];
    } else {
        if(!$A.util.isArray(fields)){
            fields = [fields];
        }
    }
    return fields
        .filter(function (i) {
            var value = i.get('v.value');
            return !value || value == '' || value.trim().length === 0;
        })
        .map(function (i) {
            return i.get('v.fieldName');
        });
},

How this works:

  1. When recordEditForm callback loads, there's a param recordUI which holds some metadata info about the form. Store that on component.
  2. Mark one or more lightning:inputField with aura:id="requiredField" and class="customRequired".
  3. On submit it will look for all required fields, will return blank ones API name's and then will check recordUiCache for their corresponding labels which will be shown as a toast (via helper.reportUserError not implemented here).
0

If you wanna validate on change, you'd go:

    <lightning:inputField
        fieldName="Listing_Type__c"
        onchange="{!c.listingChange}"/>

Controller:

    listingChange: function (component, event, helper) {
        var value = event.getParam('value');
        var isUnListed = value === 'Unlisted' || !value;
        component.set('v.isListed', !isUnListed);
    },

Can't remember if you can get field API name from the event here. Looking at my old code - I guess no. But if you could then you could just write one generic onchange handler rather one for each field.

You must log in to answer this question.

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