Input Data Using Forms

Learning Objectives

After completing this unit, you’ll be able to:
  • Create a form to display current values and accept new user input.
  • Read values from form elements.
  • Validate user input and display error messages for invalid input.
  • Refactor code from a component’s controller to its helper.

Input Data Using Forms

As of this unit, we’re done with helloWhatever-style components. From here, we’ll be creating and assembling the expenses tracker mini-app that we previewed earlier. We’ll spend the majority of this unit building and understanding the form that lets you create a new expense.

The expenses App Container

But before we get started with that, let’s also be done with creating plain, or downright ugly, components. The first thing we’ll do, then, is pull in the Salesforce Lightning Design System, or SLDS, and “activate” it in our app. The way we’ll do this lets us talk a little more about app containers.

Note

Note

We’re not actually going to discuss SLDS itself in this unit, or anywhere in the rest of the module. We’re going to focus here on getting it added to an app, and then within our example code we’ll use the SLDS classes, but not explain them in detail. See Resources for lots of different ways to learn more about SLDS.

Today, SLDS is automatically available to your components when they run inside Lightning Experience or the Salesforce app. We sometimes call this running in the one.app container. This built-in version is the same that’s used by many of the standard Lightning components. However, SLDS is not available by default in a stand-alone app, or when you use your components in Lightning Out or Lightning Components for Visualforce. These are different app containers, and they provide different services and resources. We’d like to create our expense app in such a way that it works and looks good in all of these contexts. And fortunately it’s not actually that hard to do.

The way we’ll do it is by adding SLDS to our harness application. Then, inside the “real” expenses app (really, the top-level component, and all its child components), we can use SLDS tools and techniques, without worrying about where the SLDS resources—style sheets, icons, and so on—come from. That is to say, our app container (the harness app) sets up resources inside its context so that any app running inside that container has the resources it needs.

Let’s convert those wordy concepts to some code. Create a new expensesApp.app Lightning application with the following markup.

<aura:application extends="force:slds">
       
        <!-- This component is the real "app" -->
        <!-- c:expenses/ -->

</aura:application>

Here’s what’s going on. The extends="force:slds" attribute activates SLDS in this app, by including the same Lightning Design System styles provided by Lightning Experience and the Salesforce app. But note that this harness app is just a wrapper, a shell. The real app is the expenses component, which we haven’t created. (That’s the <!-- c:expenses/ --> part; it’s commented out because we can’t save our app until the expenses component actually exists.)

By way of the wrapper app, our components use the extends="force:slds" mechanism to get access to SLDS when they run from this application. When they run inside Lightning Experience or the Salesforce app, with no code changes, they use that container’s automatic inclusion of SLDS.

In this case, that amounts to the same thing. But this concept of using the outer harness app to set up a context so that the real app doesn’t need to worry about context differences isn’t limited to style resources. You can use this to provide replacement event handlers, for example…although that’s getting ahead of ourselves. Let’s learn to walk before we try to fly!

The expenses App Component

The next step is to create the component that’s the top level for our expenses app. (Remember, even though we call it an “app,” it’s really just another Lightning component.) In the Developer Console, create a new Lightning component named “expenses”, and replace the default markup with the following.

<aura:component>

    <!-- PAGE HEADER -->
    <lightning:layout class="slds-page-header slds-page-header--object-home">
        <lightning:layoutItem>
            <lightning:icon iconName="standard:scan_card" alternativeText="My Expenses"/>
        </lightning:layoutItem>
        <lightning:layoutItem padding="horizontal-small">
            <div class="page-section page-header">
                <h1 class="slds-text-heading--label">Expenses</h1>
                <h2 class="slds-text-heading--medium">My Expenses</h2>
            </div>
        </lightning:layoutItem>
    </lightning:layout>
    <!-- / PAGE HEADER -->

    <!-- NEW EXPENSE FORM -->
    <lightning:layout>
        <lightning:layoutItem padding="around-small" size="6">

        <!-- [[ expense form goes here ]] -->

        </lightning:layoutItem>
    </lightning:layout>
    <!-- / NEW EXPENSE FORM -->

</aura:component>

What we’re creating here is the page header using the grid layout provided by the <lightning:layout> and <lightning:layout> components. size="6" creates a <div> container that’s 50% the total width (or size 6 of 12). As you might have noticed, components in the lightning namespace resemble components in Lightning Experience and the Salesforce app. Beyond buttons and layouts, you’ll find many other useful components in this namespace that work seamlessly with SLDS styling out-of-the-box.

Note

Note

Notice the <lightning:icon> tag? This component renders your favorite SLDS icons in a snap. Gone are the days when you had to create a helper component for displaying SLDS icons.

Now you can uncomment the <c:expenses/> tag in the actual .app, and open the preview of what is for now just an empty shell. You should see something like the following.

Basic My Expenses form

Not a lot going on yet, but it’s kind of exciting to see the SLDS styling already having an effect. Remember that we’re not going to explain most of the SLDS markup, but we’ll include comments in the markup. You can see how we created the header for the app, and start to get the idea.

The New Expense Form

Before we start on the form, let’s just acknowledge something upfront: What we’re about to do is temporary. Remember all that talk talk talk about decomposing your app down into separate, smaller components, and then building up from there? We’re not doing that here—not yet—and frankly it’s a bit of a cheat.

But it’s a cheat in service to keeping the code from getting too complicated, too quickly. We’re doing it this way so we can focus on one lesson at a time. And, this isn’t a bad way for you to work on things yourself: build inside one component until it gets too “busy,” and then refactor and decompose into smaller sub-components. As long as you remember to refactor!

OK, </preaching>. In expenses component, replace the <!-- [[ expense form goes here ]] --> comment with the following code for the Add Expense form.

    <!-- CREATE NEW EXPENSE -->
    <div aria-labelledby="newexpenseform">

        <!-- BOXED AREA -->
        <fieldset class="slds-box slds-theme--default slds-container--small">

        <legend id="newexpenseform" class="slds-text-heading--small 
          slds-p-vertical--medium">
          Add Expense
        </legend>
  
        <!-- CREATE NEW EXPENSE FORM -->
        <form class="slds-form--stacked">          
            <lightning:input aura:id="expenseform" label="Expense Name"
                             name="expensename"
                             value="{!v.newExpense.Name}"
                             required="true"/> 
            <lightning:input type="number" aura:id="expenseform" label="Amount"
                             name="expenseamount"
                             min="0.1"
                             formatter="currency"
                             step="0.01"
                             value="{!v.newExpense.Amount__c}"
                             messageWhenRangeUnderflow="Enter an amount that's at least $0.10."/>
            <lightning:input aura:id="expenseform" label="Client"
                             name="expenseclient"
                             value="{!v.newExpense.Client__c}"
                             placeholder="ABC Co."/>
            <lightning:input type="date" aura:id="expenseform" label="Expense Date"
                             name="expensedate"
                             value="{!v.newExpense.Date__c}"/>
            <lightning:input type="checkbox" aura:id="expenseform" label="Reimbursed?"  
                             name="expreimbursed"
                             checked="{!v.newExpense.Reimbursed__c}"/>
            <lightning:button label="Create Expense" 
                              class="slds-m-top--medium"
                              variant="brand"
                              onclick="{!c.clickCreate}"/>
        </form>
        <!-- / CREATE NEW EXPENSE FORM -->
  
      </fieldset>
      <!-- / BOXED AREA -->

    </div>
    <!-- / CREATE NEW EXPENSE -->

That looks like a lot of code to grasp at once. It’s not. When you strip away the SLDS markup and classes, this form boils down to a series of input fields and a button for form submission.

Here’s the resulting form.

New Expense form

Note

Note

<lightning:input> is the Swiss army knife for input fields that’s infused with the goodness of SLDS styling. Use it whenever you find yourself reaching for the <ui:input> component variety like <ui:inputText>, <ui:inputNumber>, and others. Components in the ui namespace don’t come with SLDS styling and are considered to be legacy components.

First, notice that we’re creating multiple instances of the <lightning:input> component with specific data types. That is, you’re not surprised that you’d want to use type="date" with a date field, and so on. There’s a range of different types, well beyond the ones specified, and it’s always best to match the component type to the data type. If you don’t specify a type, it defaults to text. The reason might not be obvious yet, but it will be when you try this app out on a phone—type-specific components can provide input widgets best suited to the form factor. For example, the date picker is optimized for mouse or finger tip, depending on where you access it.

Next, notice that each input component has a label set on it, and that the label text is automatically displayed next to the input field. There are a few other attributes we haven’t seen before: required, placeholder, type, min, and step. Most of these attributes are similar to their HTML counterparts. For example, min specifies the minimum value for the input. If you can’t guess what these are for, you can look them up in the Lightning Components Developer Guide. (And we’ll come back to that deceptive required.)

Next, there’s an aura:id attribute set on each tag. What’s that for? It sets a (locally) unique ID on each tag it’s added to, and that ID is how you’ll read values out of the form fields. The fields all share the same ID in this example so that we can access them as an array for field validation. We’ll look at how to do that very shortly.

Attributes for Salesforce Objects (sObjects)

But first we need to look at the value attribute. Each tag has a value on it, set to an expression. For example, {!v.newExpense.Amount__c}. From the format of the expression, you should be able to deduce a few things.

  • v means this is a property of the view value provider. That means that this is an attribute on the component. (Which we haven’t created yet.)
  • Based on the dot notation, you can tell that newExpense is some kind of structured data type. That is, newExpense itself has properties. Or...fields?
  • From the “__c” that’s at the end of most of the property names, you can guess that these map back to custom fields, most likely on the Expense custom object.
  • So, newExpense is probably an Expense object!

Cool, we haven’t discussed this yet! Here’s the actual attribute definition, which you should add to the top of the component, right after the opening <aura:component> tag.

    <aura:attribute name="newExpense" type="Expense__c"
         default="{ 'sobjectType': 'Expense__c',
                        'Name': '',
                        'Amount__c': 0,
                        'Client__c': '',
                        'Date__c': '',
                        'Reimbursed__c': false }"/>

What’s going on here is actually pretty simple. The name attribute you already know. And the type is, unsurprisingly, the API name of our custom object. So far, so good.

The default attribute isn’t new, but the format of its value is. But it shouldn’t be too hard to grasp. It’s a JSON representation of an sObject, specifying the type of the object (again, the API name), and values for each of the fields to set on it by default. In this case, we’re basically setting everything to a representation of an empty value.

And that’s, well, most of what you need to know about sObjects! From here out, the Lightning Components framework will let you treat newExpense, in JavaScript and in markup, like it’s a record from Salesforce—even though we’re not (yet) loading it from Salesforce!

Handle Form Submission in an Action Handler

So we have a form. Right now, if you fill it out and click the button to create a new expense, what happens? Unless you’ve run ahead and created it already, you’ll get another error about a missing controller action. This is because neither the controller, nor the action handler specified on the <lightning:button>, have been created.

In the Developer Console, click the CONTROLLER button for the expenses component to create the controller resource. Then replace the default code with the following.

({
    clickCreate: function(component, event, helper) {
        var validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
            // Displays error messages for invalid fields
            inputCmp.showHelpMessageIfInvalid();
            return validSoFar && inputCmp.get('v.validity').valid;
        }, true);
        // If we pass error checking, do some real work
        if(validExpense){
            // Create the new expense
            var newExpense = component.get("v.newExpense");
            console.log("Create expense: " + JSON.stringify(newExpense));
            helper.createExpense(component, newExpense);
        }
    }
})

OK, this is all new, so let’s look at it carefully. First, let’s note that this action handler function is basically divided into three sections, or steps:

  1. Setup
  2. Process form values
  3. If there are no errors, do something

This structure might be familiar to you, because it’s a pretty fundamental way to process user input in a web application. Let’s look at each of the steps, to see how they work in the Lightning component.

For setup, all we do is initialize the state of our error checking. It’s a simple flag, is this a valid expense? Each time the clickCreate action handler is called, we’ll start on the assumption that the expense data is OK, and then invalidate it if we find a problem. Here’s a rundown of the validExpense flag, with an initial value that’s set to true.

  • component.find('expenseform') gets a reference to the array of <lightning:input> fields that require validation. If the ID is unique, the reference returns the component. In this case, the ID is not unique and the reference returns an array of components.
  • The JavaScript reduce() method reduces the array to a single value that’s captured by validSoFar, which remains true until it finds an invalid field, changing validSoFar to false. An invalid field can be a required field that’s empty, a field that has a number lower than a specified minimum number, among many others.
  • inputCmp.get('v.validity').valid returns the validity of the current input field in the array.
  • inputCmp.showHelpMessageIfInvalid() displays an error message for invalid fields. <lightning:input> provides default error messages that can be customized by attributes like messageWhenRangeUnderflow, which you’ve seen in the expense form example.

Let’s get into some interesting details. Back in helloMessageInteractive, we didn’t use find() to figure out the label text of the button that was clicked. This is because we didn’t need to. We were able to get a reference to that button directly, by pulling it out of the event parameter using event.getSource(). You don’t always have that luxury; indeed, it’s rare when everything you need from user input comes only from the event.

So, when your controller needs a way to get to a child component, first set an aura:id on that component in markup, and then use component.find(theId) to get a reference to the component at runtime.

Note

Note

component.find() only lets you access direct child components. It isn’t a magic way to go wandering around the component hierarchy and read or change things. Remember, components are supposed to be self-contained, or to communicate with…well, we’ll get to that.

Validation with <lightning:input> harnesses the power of the underlying HTML input element to process form values so you don’t have to in most cases. Need to validate a telephone number? Use type="tel" and define the pattern attribute using a regular expression. Need to validate a percentage value? Use type="number" with formatter="percent". Need to validate something else? Let <lightning:input> do the heavy lifting for you.

It’s when validation fails that things get interesting again. When the user enters invalid input, we want two things to happen:

  1. Don’t try to create the expense.
  2. Show a helpful error message.

For the first, we set the validExpense flag to false when the field validity referenced by inputCmp.get('v.validity').valid evaluates to false. For the second, we make use of the built-in validation errors or provide custom messages for the errors. In the expense form, the required name field displays “Complete this field” if the field is empty and you try to submit the form. But you can provide your own custom message by specifying messageWhenValueMissing="Did you forget me?"

By contrast, if the field passes validation, the validExpense flag evaluates to true and no errors are displayed.

And with that, we’re on to step three in handling the form submission: actually creating the expense! As you can see, to prepare for that, we’re getting the complete newExpense object from the component attribute: component.get("v.newExpense"). This gives us a single variable that we can use to create a new expense record.

Before we get to that, though, here’s a question for you to think about: why don’t we pull the form values out of newExpense? Get the structured variable once, at the start of the action handler, and then just access its properties, instead of what could be a long series of find().get() calls?

The reason is simple: because we need references to the individual fields to be able to call showHelpMessageIfInvalid() on them. It’s also a good practice to validate the raw form data; your validation logic doesn’t know what kinds of processing might happen within the newExpense object.

Create the New Expense

Remember earlier we said that putting the expense form in the main component was a bit of a cheat? Well, this next section doesn’t get the “a bit” qualifier. What we’re doing here is just flat out avoiding the complexity of really creating the record. And we’re avoiding it now because that’s the entire next unit. So, let’s wrap this one up with something simple, but which still shows us some important concepts.

First, let’s create a place to “store” new expenses. We’ll simply create a local-only array of expenses to hold them. At the top of expenses component markup, right before the newExpense attribute, add a new expenses attribute, which will hold an array of expense objects.

    <aura:attribute name="expenses" type="Expense__c[]"/>

All we need to do is update the expenses array. Which, it turns out, is easy (in the cheater-cheater version of our form), and illustrates yet another important concept.

In our controller, we’ve hidden the work of actually creating the new expense behind this function call: helper.createExpense(component, newExpense). In software development, another word for “hiding” is abstraction. And we’re using something called a helper to abstract away our cheating.

We discussed helpers briefly earlier, and we’re not going to cover advanced details of helpers in this module. For now, let’s say three things about helpers:

  • A component’s helper is the appropriate place to put code to be shared between several different action handlers.
  • A component’s helper is a great place to put complex processing details, so that the logic of your action handlers remains clear and streamlined.
  • Helper functions can have any function signature. That is, they’re not constrained the way that action handlers in the controller are. (Why is this? Because you are calling the helper function directly from your code. By contrast, the framework calls action handlers via the framework runtime.) It’s a convention and recommended practice to always provide the component as the first parameter to helper functions.

OK, let’s get on with it. In the Developer Console, click the HELPER button for the expenses component to create the associated helper resource, and then replace the example code with the following.

({
    createExpense: function(component, expense) {
        var theExpenses = component.get("v.expenses");
 
        // Copy the expense to a new object
        // THIS IS A DISGUSTING, TEMPORARY HACK
        var newExpense = JSON.parse(JSON.stringify(expense));
 
        theExpenses.push(newExpense);
        component.set("v.expenses", theExpenses);
    }
})

For the moment, ignore the disgusting hack part. The other three lines of code illustrate a common pattern we’ve seen before, and which you’ll use over and over: get-process-set. First we get the array of expenses from the expenses attribute. Then we add the new expense “record” to it. Then we update (set) the expenses attribute with the changed array.

The Reference Is Not the Collection

What’s new here is that, for the first time, we’re updating a collection, an array. And if you’re an experienced programmer, you’re probably wondering: “Why do I need the set() here?”

That is, component.get("v.expenses") gets a reference to the array stored in the component attribute. component.set("v.expenses", theExpenses) simply sets the component attribute to the same reference. Sure, in between, the contents of the array has been added to, but the container is the same: the reference to the array hasn’t actually changed! So, why update it?

If you’re having trouble understanding what this means, add two logging statements before and after the critical statements, and dump the contents of theExpenses to the console.

console.log("Expenses before 'create': " + JSON.stringify(theExpenses));
theExpenses.push(newExpense);
component.set("v.expenses", theExpenses);
console.log("Expenses after 'create': " + JSON.stringify(theExpenses));

Reload and run the app, add at least two expenses, and look at the structure of theExpenses. Now comment out the component.set() line, and do it again.

What the...? component.set() doesn’t affect theExpenses at all! But! But! But? What does it actually do?!?

You are absolutely correct to be asking this question. And the answer is: magic!

What component.set() does here isn’t update the value of the expenses attribute. It triggers notification that the expenses attribute has changed.

The effect is that, anywhere in your app that you’ve referenced the expenses attribute in an expression, the value of that expression is updated, and that update cascades everywhere the expenses attribute was used. And they all update to rerender based on the new contents. This all happens behind the scenes, handled by the Lightning Components framework, as part of the auto-wiring that happens when you use {!v.expenses}. In a word, magic.

To summarize, if this were plain JavaScript, you wouldn’t need the component.set(). In order to trigger underlying effects built into Lightning Components, you do. If you ever write some controller or helper code, test it, and nothing happens, make sure you’ve done the required component.set().

The “disgusting hack” works around a similar issue with references. To see the issue, change the line to remove the two JSON calls, and test the app. You’ll see what the problem is quickly enough. We’ll remove it in the next unit, and so won’t explain further.

Displaying the List of Expenses

So, for all that talk about “magically” updating anything that uses {!v.expenses}, guess what. Nothing else is using it, yet. Let’s fix that.

In the Developer Console, create a new Lightning component named expenseItem, and replace the default markup with the following. If you already created expenseItem, just update the markup. You’ve seen the expressions that access fields on the expense record before. This version includes SLDS markup to make it more stylish. Also, using the {!v.expense.Reimbursed__c ? 'slds-theme--success' : ''} expression sets the color green on the container when the expense has been reimbursed.
<aura:component>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:attribute name="formatdate" type="Date"/>
    <aura:attribute name="expense" type="Expense__c"/>


    <lightning:card title="{!v.expense.Name}" iconName="standard:scan_card"
                    class="{!v.expense.Reimbursed__c ?
                           'slds-theme--success' : ''}">
        <aura:set attribute="footer">
            <p>Date: <lightning:formattedDateTime value="{!v.formatdate}"/></p>
            <p class="slds-text-title"><lightning:relativeDateTime value="{!v.formatdate}"/></p>
        </aura:set>
        <p class="slds-text-heading--medium slds-p-horizontal--small">
           Amount: <lightning:formattedNumber value="{!v.expense.Amount__c}" style="currency"/>
        </p>
        <p class="slds-p-horizontal--small">
            Client: {!v.expense.Client__c}
        </p>
        <p>
            <lightning:input type="toggle" 
                             label="Reimbursed?"
                             name="reimbursed"
                             class="slds-p-around--small"
                             checked="{!v.expense.Reimbursed__c}"
                             messageToggleActive="Yes"
                             messageToggleInactive="No"
                             onchange="{!c.clickReimbursed}"/>
        </p>
    </lightning:card>
</aura:component>

Next, create a client-side controller expenseItemController.js with the following code. Here we are converting the date that’s returned by the server later to a JavaScript Date object, so that it’s displayed correctly by <lightning:formattedDateTime> and <lightning:relativeDateTime>. This conversion is handled during component initialization, captured by the <aura:handler> tag. This is a useful way to handle the initialization event and we’ll use this again later when we load data from Salesforce.

({
    doInit : function(component, event, helper) {
        var mydate = component.get("v.expense.Date__c");
        if(mydate){
            component.set("v.formatdate", new Date(mydate));
        }
    },
})
In the Developer Console, create a new Lightning component named expensesList, and replace the default markup with the following.
<aura:component>

    <aura:attribute name="expenses" type="Expense__c[]"/>

    <lightning:card title="Expenses">
        <p class="slds-p-horizontal--small">
            <aura:iteration items="{!v.expenses}" var="expense">
                <c:expenseItem expense="{!expense}"/>
            </aura:iteration>
        </p>
    </lightning:card>

</aura:component>

There’s not a lot new for you here. This is a component that displays a list of expenses. It has one attribute, expenses, which is an array of expense (Expense__c) objects. And it uses an <aura:iteration> to create an <c:expenseItem> for each of those expense objects. The overall effect is, as we noted, to display a list of expenses. So far, so good.

Now add the expensesList component to the end of expenses component. Add it right before the ending </aura:component> tag in expenses.cmp.

<c:expensesList expenses="{!v.expenses}"/>

If you reload the application, you’ll see an Expenses section below the form. (It’s not quite right, visually, but it’s good enough for now.)

What did we just do? We added the expensesList component to and passed the main expenses attribute into it. So now the instance of expenses that expensesList has is the same as the instance of expenses that the expenses component has. They are references to the same array of records, and through the magic of Lightning Components, when the main expenses array is updated, the expensesList component will “notice” and rerender its list. Give it a whirl!

Whew! That was a long unit, and it calls for a stretch break. Please, do get up and walk around for a few minutes.

Then come back, and we’ll show you how to save your new expenses for real.

retargeting