SFDC Stop - Always the latest about Salesforce


Full Tutorial Series with videos, free apps, live sessions, salesforce consulting and much more.


Telegram logo   Join our Telegram Channel

Monday, 30 August 2021

Custom validation in Lightning Web Component | Understanding Regular Expressions (Regex)

Hello Trailblazers,

In this post we're going to learn how we can apply custom validation to fields in LWC. We're going to create a simple New Contact Form and then we'll apply validation to each field one by one. Let's get started and have a look at the basic form below:

Tutorial Video

HTML Code

<template>
    <lightning-card title="Sign Up">
        <p class="slds-var-p-around_small">
            <lightning-layout multiple-rows>
                <lightning-layout-item padding="around-small" size="12" medium-device-size="6" large-device-size="6">
                    <lightning-input name="firstName" class="validate" type="text" label="First Name" required></lightning-input>
                    <lightning-input name="password" class="validate" type="password" label="Password" required></lightning-input>
                    <lightning-input name="email" class="validate" type="email" label="Email" required></lightning-input>
                    <lightning-combobox name="country" class="validate" label="Country" options={countryOptions} required></lightning-combobox>
                </lightning-layout-item>
                <lightning-layout-item padding="around-small" size="12" medium-device-size="6" large-device-size="6">
                    <lightning-input name="lastName" class="validate" type="text" label="Last Name" required></lightning-input>
                    <lightning-input name="company" class="validate" type="text" label="Company" required></lightning-input>
                    <lightning-input name="phone" class="validate" type="tel" label="Phone" required></lightning-input>
                    <lightning-input name="pincode" class="validate" type="number" label="Pin Code"></lightning-input>
                </lightning-layout-item>
                <lightning-layout-item size="12" class="slds-var-p-top_small">
                    <lightning-button class="slds-align_absolute-center" label="Sign Up" onclick={createContact}></lightning-button>    
                </lightning-layout-item>
            </lightning-layout>
        </p>
    </lightning-card>
</template>
As you can see above, we have a contact form which is used to create a new record based on the information entered. We have a couple of fields here namely: First Name, Last Name, Password, Company, Email, Phone, Country and Pin Code. Some validations are pretty basic and easier to apply. For example: I want some of the fields to be required, so I have mentioned the required property while defining lightning-input to make them required. Notice that we don't have to add something like required="true" here because required is a boolean property and we can set it as true by just mentioning it in the tag.

Another thing to notice here is, all the fields that I want to validate are specified with a class named validate because I am going to use the same class name to refer these fields in JavaScript in order to perform the validation. Each of the lightning-input is having some general attributes here like: name, type, label etc. The lightning-combobox that we have in the form is used by the user to select a country from a list of countries, that's why it's referring to countryOptions which we're going to define in js, in the options property. Finally, the lightning-button is going to call createContact() method in js when it's clicked.

Before moving onto the js, let's add more validation for some of the fields:

Setting up validation for Email field

Let's start with the email field, as of now we're checking that the user should enter a text in the email field and that's it. But we need to make sure that the email entered is a valid email and for that, we need to create a regex (regular expression) with which we can match the entered text to ensure if it's correct or not.

Building a Regex

Building a simple regex is fairly easy. First of all let's list down the criterias that our email field value should fulfil:

  1. Email can start with characters between A-Z, a-z, 0-9, can have an underscore "_", a hyphen "-", or a dot "." in between. For example: rahul.malhotra, RAHUL.MALHOTRA, rahul123.malhotra, rahul_malhotra, rahul-malhotra etc.

  2. After that it should be followed by an @ symbol and we should allow only these characters post @: a-z, 0-9 and a hyphen "-". For example: rahul.malhotra@example, rahul.malhotra@sfdc12, rahul.malhotra@sfdc-stop etc.

  3. Finally, we should have a dot "." followed by a minimum of 2 characters in the range: a-z. For example: rahul.malhotra@sfdcstop.com, rahul@example.in etc.

So, that's how the full email address should be formed fulfilling these conditions. Let's start creating regex now:

1. The first point specifies, I can include capital A to Z, small a to z, numbers 0-9 and some special characters like: "_", "-" and "." So, the regex will be: "[A-Za-z0-9._-]+". If you notice all the acceptable characters are included in square followed by a + which means that we can have 1 or more occurence of any of these characters. This regex will validate the part of email before @ symbol.

2. Then we should have an @ symbol, let's add that to regex so it becomes: "[A-Za-z0-9._-]+@" and as per point 2 in the previous points, I can have only small alphabets a to z and numbers 0 to 9 including a hyphen after @. So, the regex for that will be: "[a-z0-9-]+". I hope this is clear to you know as it's similar to what we created in point 1 above, we just added all acceptable characters in square brackets followed by a +. The regex combining everything we discussed till now is: "[A-Za-z0-9._-]+@[a-z0-9-]+"

3. Now, as per point 3, we should have a dot "." followed by two or more characters in the range of lowercase alphabets a to z. So, the regex for this will be: "\.[a-z]{2,}$". As you can see first of all we have a dot escaped by "\" character as: "\." . This is because dot is a reserved character in regex which means "any character". After dot, we have lowercase a to z in square brackets and then we have curly brackets as {2,} which means we should have two or more occurences of the characters specified in square brackets. So, our regex is complete and each regex always ends with a $ to specify the end, therefore we have added a $ at the end.

The final regex for our email validation is: [A-Za-z0-9._-]+@[a-z0-9-]+\.[a-z]{2,}$

We can specify a regex by using the pattern property of the lightning-input, so our updated email field will be:
<lightning-input name="email" class="validate" type="email" label="Email" pattern="[A-Za-z0-9._-]+@[a-z0-9-]+\.[a-z]{2,}$" message-when-pattern-mismatch="Please enter a valid email" required></lightning-input>
As you can see above, I have added my regex in the pattern field and have added another property named as message-when-pattern-mismatch with a value: "Please enter a valid email". The message-when-pattern-mismatch property is used to specify an error message when the input entered by user is not matching the pattern specified in the pattern property.

Our email field is fixed now, let's add a validation for our phone field as well:

Setting up validation for Phone field

For phone field, a simple phone number can be in the format: 123-456-7890. That means, we can have 3 numbers followed by a hyphen "-", followed by another 3 numbers and then again a hyphen "-" followed by 4 numbers at the end.

Therefore, the regex will be: [0-9]{3}-[0-9]{3}-[0-9]{4}$

Here, [0-9]{3} represents: we're allowing 3 characters in the range 0-9. Similarly for 4 numbers we have specified [0-9]{4}.

For the error message, we're going to specify Please enter a valid phone number as a value for the message-when-pattern-mismatch property. The updated phone field will be:
<lightning-input name="phone" class="validate" type="tel" label="Phone" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}$" message-when-pattern-mismatch="Please enter a valid phone number" required></lightning-input>
Our phone field is also fixed now, let's add a validation for pin code as well.

Setting up validation for Pin Code field

For Pin Code field, we're going to setup a minimum and maximum value for pin code and set the error messages accordingly. lightning-input has min and max property so let's say we can have any value starting with a 4 digit number upto 6 digit number in the pin code. So, we're going to set min as 1000 and max as 999999. We also have other properties to setup if the user enters a value outside the range. message-when-range-overflow property is used to specify an error message which will be displayed when a value is greater than the value specified in max. Similarly, message-when-range-underflow property is used to specify an error message which will be displayed when a value is less than the value specified in min.

So, the updated pin code field will be:
<lightning-input name="pincode" class="validate" type="number" label="Pin Code" min="1000" max="999999" message-when-range-overflow="Please enter a correct pincode" message-when-range-underflow="Please enter a correct pincode"></lightning-input>

Let's have a look at our updated form as a whole:
<template>
    <lightning-card title="Sign Up">
        <p class="slds-var-p-around_small">
            <lightning-layout multiple-rows>
                <lightning-layout-item padding="around-small" size="12" medium-device-size="6" large-device-size="6">
                    <lightning-input name="firstName" class="validate" type="text" label="First Name" required></lightning-input>
                    <lightning-input name="password" class="validate" type="password" label="Password" required></lightning-input>
                    <lightning-input name="email" class="validate" type="email" label="Email" pattern="[A-Za-z0-9._-]+@[a-z0-9-]+.[a-z]{2,}$" message-when-pattern-mismatch="Please enter a valid email" required></lightning-input>
                    <lightning-combobox name="country" class="validate" label="Country" options={countryOptions} required></lightning-combobox>
                </lightning-layout-item>
                <lightning-layout-item padding="around-small" size="12" medium-device-size="6" large-device-size="6">
                    <lightning-input name="lastName" class="validate" type="text" label="Last Name" required></lightning-input>
                    <lightning-input name="company" class="validate" type="text" label="Company" required></lightning-input>
                    <lightning-input name="phone" class="validate" type="tel" label="Phone" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}$" message-when-pattern-mismatch="Please enter a valid phone number" required></lightning-input>
                    <lightning-input name="pincode" class="validate" type="number" label="Pin Code" min="1000" max="999999" message-when-range-overflow="Please enter a correct pincode" message-when-range-underflow="Please enter a correct pincode"></lightning-input>
                </lightning-layout-item>
                <lightning-layout-item size="12" class="slds-var-p-top_small">
                    <lightning-button class="slds-align_absolute-center" label="Sign Up" onclick={createContact}></lightning-button>    
                </lightning-layout-item>
            </lightning-layout>
        </p>
    </lightning-card>
</template>

JavaScript Code

I hope you understood how we've implemented regex in order to validate the fields, now it's time to add some JavaScript code to validate all these fields on button click. Let's have a look at the below JavaScript code:
import { LightningElement } from 'lwc';

export default class ContactForm extends LightningElement {

    contact = {};
    countryOptions = [
        { "label": "Afghanistan", "value": "Afghanistan" },
        { "label": "Albania", "value": "Albania" },
        { "label": "Algeria", "value": "Algeria" },
        { "label": "Andorra", "value": "Andorra" },
        { "label": "Angola", "value": "Angola" },
        { "label": "Antigua & Deps", "value": "Antigua  & Deps"},
        { "label": "Argentina", "value": "Argentina" },
        { "label": "Armenia", "value": "Armenia" },
        { "label": "Australia", "value": "Australia" },
        { "label": "Austria", "value": "Austria" },
        { "label": "Azerbaijan", "value": "Azerbaijan" },
        { "label": "Bahamas", "value": "Bahamas" },
        { "label": "Bahrain", "value": "Bahrain" },
        { "label": "Bangladesh", "value": "Bangladesh" },
        { "label": "Barbados", "value": "Barbados" },
        { "label": "Belarus", "value": "Belarus" },
        { "label": "Belgium", "value": "Belgium" },
        { "label": "Belize", "value": "Belize" },
        { "label": "Benin", "value": "Benin" },
        { "label": "Bhutan", "value": "Bhutan" },
        { "label": "Bolivia", "value": "Bolivia" },
        { "label": "Bosnia Herzegovina", "value": "Bosnia  Herzegovina"},
        { "label": "Botswana", "value": "Botswana" },
        { "label": "Brazil", "value": "Brazil" },
        { "label": "Brunei", "value": "Brunei" },
        { "label": "Bulgaria", "value": "Bulgaria" },
        { "label": "Burkina", "value": "Burkina" },
        { "label": "Burundi", "value": "Burundi" },
        { "label": "Cambodia", "value": "Cambodia" },
        { "label": "Cameroon", "value": "Cameroon" },
        { "label": "Canada", "value": "Canada" },
        { "label": "Cape Verde", "value": "Cape  Verde"},
        { "label": "Central African Rep", "value": "Central  African Rep"},
        { "label": "Chad", "value": "Chad" },
        { "label": "Chile", "value": "Chile" },
        { "label": "China", "value": "China" },
        { "label": "Colombia", "value": "Colombia" },
        { "label": "Comoros", "value": "Comoros" },
        { "label": "Congo", "value": "Congo" },
        { "label": "Congo {Democratic Rep}", "value": "Congo  {Democratic Rep}"},
        { "label": "Costa Rica", "value": "Costa  Rica"},
        { "label": "Croatia", "value": "Croatia" },
        { "label": "Cuba", "value": "Cuba" },
        { "label": "Cyprus", "value": "Cyprus" },
        { "label": "Czech Republic", "value": "Czech  Republic"},
        { "label": "Denmark", "value": "Denmark" },
        { "label": "Djibouti", "value": "Djibouti" },
        { "label": "Dominica", "value": "Dominica" },
        { "label": "Dominican Republic", "value": "Dominican  Republic"},
        { "label": "East Timor", "value": "East  Timor"},
        { "label": "Ecuador", "value": "Ecuador" },
        { "label": "Egypt", "value": "Egypt" },
        { "label": "El Salvador", "value": "El  Salvador"},
        { "label": "Equatorial Guinea", "value": "Equatorial  Guinea"},
        { "label": "Eritrea", "value": "Eritrea" },
        { "label": "Estonia", "value": "Estonia" },
        { "label": "Ethiopia", "value": "Ethiopia" },
        { "label": "Fiji", "value": "Fiji" },
        { "label": "Finland", "value": "Finland" },
        { "label": "France", "value": "France" },
        { "label": "Gabon", "value": "Gabon" },
        { "label": "Gambia", "value": "Gambia" },
        { "label": "Georgia", "value": "Georgia" },
        { "label": "Germany", "value": "Germany" },
        { "label": "Ghana", "value": "Ghana" },
        { "label": "Greece", "value": "Greece" },
        { "label": "Grenada", "value": "Grenada" },
        { "label": "Guatemala", "value": "Guatemala" },
        { "label": "Guinea", "value": "Guinea" },
        { "label": "Guinea-Bissau", "value": "Guinea- Bissau"},
        { "label": "Guyana", "value": "Guyana" },
        { "label": "Haiti", "value": "Haiti" },
        { "label": "Honduras", "value": "Honduras" },
        { "label": "Hungary", "value": "Hungary" },
        { "label": "Iceland", "value": "Iceland" },
        { "label": "India", "value": "India" },
        { "label": "Indonesia", "value": "Indonesia" },
        { "label": "Iran", "value": "Iran" },
        { "label": "Iraq", "value": "Iraq" },
        { "label": "Ireland {Republic}", "value": "Ireland  {Republic}"},
        { "label": "Israel", "value": "Israel" },
        { "label": "Italy", "value": "Italy" },
        { "label": "Ivory Coast", "value": "Ivory  Coast"},
        { "label": "Jamaica", "value": "Jamaica" },
        { "label": "Japan", "value": "Japan" },
        { "label": "Jordan", "value": "Jordan" },
        { "label": "Kazakhstan", "value": "Kazakhstan" },
        { "label": "Kenya", "value": "Kenya" },
        { "label": "Kiribati", "value": "Kiribati" },
        { "label": "Korea North", "value": "Korea  North"},
        { "label": "Korea South", "value": "Korea  South"},
        { "label": "Kosovo", "value": "Kosovo" },
        { "label": "Kuwait", "value": "Kuwait" },
        { "label": "Kyrgyzstan", "value": "Kyrgyzstan" },
        { "label": "Laos", "value": "Laos" },
        { "label": "Latvia", "value": "Latvia" },
        { "label": "Lebanon", "value": "Lebanon" },
        { "label": "Lesotho", "value": "Lesotho" },
        { "label": "Liberia", "value": "Liberia" },
        { "label": "Libya", "value": "Libya" },
        { "label": "Liechtenstein", "value": "Liechtenstein" },
        { "label": "Lithuania", "value": "Lithuania" },
        { "label": "Luxembourg", "value": "Luxembourg" },
        { "label": "Macedonia", "value": "Macedonia" },
        { "label": "Madagascar", "value": "Madagascar" },
        { "label": "Malawi", "value": "Malawi" },
        { "label": "Malaysia", "value": "Malaysia" },
        { "label": "Maldives", "value": "Maldives" },
        { "label": "Mali", "value": "Mali" },
        { "label": "Malta", "value": "Malta" },
        { "label": "Marshall Islands", "value": "Marshall  Islands"},
        { "label": "Mauritania", "value": "Mauritania" },
        { "label": "Mauritius", "value": "Mauritius" },
        { "label": "Mexico", "value": "Mexico" },
        { "label": "Micronesia", "value": "Micronesia" },
        { "label": "Moldova", "value": "Moldova" },
        { "label": "Monaco", "value": "Monaco" },
        { "label": "Mongolia", "value": "Mongolia" },
        { "label": "Montenegro", "value": "Montenegro" },
        { "label": "Morocco", "value": "Morocco" },
        { "label": "Mozambique", "value": "Mozambique" },
        { "label": "Myanmar, {Burma}", "value": "Myanmar,  {Burma}"},
        { "label": "Namibia", "value": "Namibia" },
        { "label": "Nauru", "value": "Nauru" },
        { "label": "Nepal", "value": "Nepal" },
        { "label": "Netherlands", "value": "Netherlands" },
        { "label": "New Zealand", "value": "New  Zealand"},
        { "label": "Nicaragua", "value": "Nicaragua" },
        { "label": "Niger", "value": "Niger" },
        { "label": "Nigeria", "value": "Nigeria" },
        { "label": "Norway", "value": "Norway" },
        { "label": "Oman", "value": "Oman" },
        { "label": "Pakistan", "value": "Pakistan" },
        { "label": "Palau", "value": "Palau" },
        { "label": "Panama", "value": "Panama" },
        { "label": "Papua New Guinea", "value": "Papua  New Guinea"},
        { "label": "Paraguay", "value": "Paraguay" },
        { "label": "Peru", "value": "Peru" },
        { "label": "Philippines", "value": "Philippines" },
        { "label": "Poland", "value": "Poland" },
        { "label": "Portugal", "value": "Portugal" },
        { "label": "Qatar", "value": "Qatar" },
        { "label": "Romania", "value": "Romania" },
        { "label": "Russian Federation", "value": "Russian  Federation"},
        { "label": "Rwanda", "value": "Rwanda" },
        { "label": "St Kitts & Nevis", "value": "St  Kitts & Nevis"},
        { "label": "St Lucia", "value": "St  Lucia"},
        { "label": "Saint Vincent & the Grenadines", "value": "Saint  Vincent & the Grenadines"},
        { "label": "Samoa", "value": "Samoa" },
        { "label": "San Marino", "value": "San  Marino"},
        { "label": "Sao Tome & Principe", "value": "Sao  Tome & Principe"},
        { "label": "Saudi Arabia", "value": "Saudi  Arabia"},
        { "label": "Senegal", "value": "Senegal" },
        { "label": "Serbia", "value": "Serbia" },
        { "label": "Seychelles", "value": "Seychelles" },
        { "label": "Sierra Leone", "value": "Sierra  Leone"},
        { "label": "Singapore", "value": "Singapore" },
        { "label": "Slovakia", "value": "Slovakia" },
        { "label": "Slovenia", "value": "Slovenia" },
        { "label": "Solomon Islands", "value": "Solomon  Islands"},
        { "label": "Somalia", "value": "Somalia" },
        { "label": "South Africa", "value": "South  Africa"},
        { "label": "South Sudan", "value": "South  Sudan"},
        { "label": "Spain", "value": "Spain" },
        { "label": "Sri Lanka", "value": "Sri  Lanka"},
        { "label": "Sudan", "value": "Sudan" },
        { "label": "Suriname", "value": "Suriname" },
        { "label": "Swaziland", "value": "Swaziland" },
        { "label": "Sweden", "value": "Sweden" },
        { "label": "Switzerland", "value": "Switzerland" },
        { "label": "Syria", "value": "Syria" },
        { "label": "Taiwan", "value": "Taiwan" },
        { "label": "Tajikistan", "value": "Tajikistan" },
        { "label": "Tanzania", "value": "Tanzania" },
        { "label": "Thailand", "value": "Thailand" },
        { "label": "Togo", "value": "Togo" },
        { "label": "Tonga", "value": "Tonga" },
        { "label": "Trinidad & Tobago", "value": "Trinidad  & Tobago"},
        { "label": "Tunisia", "value": "Tunisia" },
        { "label": "Turkey", "value": "Turkey" },
        { "label": "Turkmenistan", "value": "Turkmenistan" },
        { "label": "Tuvalu", "value": "Tuvalu" },
        { "label": "Uganda", "value": "Uganda" },
        { "label": "Ukraine", "value": "Ukraine" },
        { "label": "United Arab Emirates", "value": "United  Arab Emirates"},
        { "label": "United Kingdom", "value": "United  Kingdom"},
        { "label": "United States", "value": "United  States"},
        { "label": "Uruguay", "value": "Uruguay" },
        { "label": "Uzbekistan", "value": "Uzbekistan" },
        { "label": "Vanuatu", "value": "Vanuatu" },
        { "label": "Vatican City", "value": "Vatican  City"},
        { "label": "Venezuela", "value": "Venezuela" },
        { "label": "Vietnam", "value": "Vietnam" },
        { "label": "Yemen", "value": "Yemen" },
        { "label": "Zambia", "value": "Zambia" },
        { "label": "Zimbabwe", "value": "Zimbabwe" }
    ];

    /* 
    *   This method is used to check if all the input fields 
    *   that we need to validate are valid or not. We're also going
    *   to populate our contact object so that it can be sent to apex
    *   in order to save the details in salesforce
    */
    isInputValid() {
        let isValid = true;
        let inputFields = this.template.querySelectorAll('.validate');
        inputFields.forEach(inputField => {
            if(!inputField.checkValidity()) {
                inputField.reportValidity();
                isValid = false;
            }
            this.contact[inputField.name] = inputField.value;
        });
        return isValid;
    }

    /* 
    *   This method is used to create a new contact in salesforce
    *   based on the values entered by the user. For now, our main
    *   purpose is validation so, we're just going to display the 
    *   contact object once it's validated to make sure that we 
    *   have all the fields and their values ready to be saved
    */
    createContact() {
        if(this.isInputValid()) {
            console.log(this.contact);
        }
    }
}
In the above code snippet, contact is just used to store the data that's entered by user. countryOptions is storing a list of countries with their label and values as these countries are displayed in lightning-combobox. After that we have two methods: isInputValid() and createContact().

isInputValid()

This method is used to validate all the fields to make sure that all the values entered are correct or not. First of all we have a variable inside this method named as isValid which is true by default. Then we're getting all the input fields using this.template.querySelectorAll('.validate'); if you remember, we've given the same class name validate to all the input fields that we want to validate. We're using the same validate class here to get references of all input fields together in order to form inputFields array.

Then, we're iterating this inputFields array using a forEach loop and for each inputField, we're first of all checking the validity using checkValidity() inside an if condition. If the input field is invalid that means, we have a validation error. We're reporting that validity by calling reportValidity() method on inputField as: inputField.reportValidity(); . This method will mainly show the error message on the field. Then, we're setting up isValid to false as the inputField is not valid. Outside the if condition, we're populating our contact object by using inputField.name as the key and inputField.value as the value. If you check the html, we've populated the name attribute for each inputField that we're using here. Finally, once the forEach loop is completed, we're returning the isValid variable (which is a boolean) from the isInputValid() method.

createContact()

As the sign up button is clicked in html, the createContact() method is called, Inside this method, first of all we're calling the isInputValid() method inside the if condition to confirm if the values entered in the form are valid or not. If the inputs are valid, we're simply displaying the contact object in the js console to verify the values.

Validation Demo


If we have filled all the field values properly, we'll see the contact object in the console as shown below:

That's all for this tutorial everyone, I hope you liked it and understood, how you can setup custom validation in lwc. Let me know if you have any questions or feedback in the comments down below. The full code for this tutorial can be found at the GitHub repository here.

Happy Trailblazing..!!

No comments:

Post a Comment