trailhead

Attributes and Expressions

Learning Objectives

After completing this unit, you’ll be able to:
  • Define attributes on your components, and pass attribute values into nested components.
  • Understand the difference between a component definition and a component instance, and create multiple instances of a component.
  • Create basic expressions to display changing and calculated values.
  • Create conditional expressions for dynamic output.

Component Attributes

To this point, while we’ve created a couple of components, and learned a fair bit about building apps with them (at a high level), the code we’ve written isn’t doing much more than what plain HTML does. That is, the two components we’ve created output the same static text, no matter what we do. You could put a dozen of them on the same screen, and they’d always say the same thing.

Boring.

To change that, we need to learn two things. First, we need to learn how to enable a component to accept input when it’s created. That is, we need to set values on the component. We do this using attributes.

(The second thing we need to learn is how to actually use these values to change a component’s behavior and output. We’ll do that after we figure out attributes.)

Attributes on components are like instance variables in objects. They’re a way to save values that change, and a way to name those value placeholders. For example, let’s say we wanted to write a helloMessage component that prints a custom message. We can envision adding a message attribute to this component to customize its output. And then we can set that message when we add the component to our app, like the following.
<aura:component>
	  
    <c:helloMessage message="You look nice today."/>
	    
</aura:component>
You’ll want to add this to your org, because we’ll use it a few more times as we go. But if you do it now you’ll get an error. Why is that? Because the helloMessage component doesn’t exist yet. Lightning components validates your code as you write it. If you try to save code that it knows is invalid—for example, referencing a non-existent component—you’ll get an error. So, let’s figure out how to create helloMessage first.

You can set a component’s attributes when you create it, as we did in the preceding example. You can also change them over the course of your component’s lifecycle, in response to actions the user takes, or events that happen elsewhere, and so on. And you can of course read and use attribute values in a number of different ways. We’ll look at those when we get to expressions.

For the moment, let’s look at how you define attributes for a component. An attribute is defined using an <aura:attribute> tag, which requires values for the name and type attributes, and accepts these optional attributes: default, description, required.

Whoa, that’s a lot of different ways to use “attribute” in a sentence! It’s really easy to get confused here, because we have three different concepts with similar names. Let’s be specific.

  1. A component attribute is the place where you can store a value. In the preceding example, the helloMessage component has a component attribute named message. Most of the time we’re talking about component attributes.
  2. You define a component attribute using the <aura:attribute> tag. We’ll see an example of that momentarily. Let’s call these attribute definitions.
  3. The <aura:attribute> tag itself takes attributes when you use it! 😖 That is, when you define a component attribute using the <aura:attribute> tag, you set attributes on <aura:attribute> that specify the “shape” of the component attribute you’re defining. 😡 Wait, let’s try again: add a component attribute definition by setting attributes on the attribute’s definition. 😭 A component attribute’s attribute definition takes attributes? 😴

This is why writers 😱. Let’s try to solve this terminology problem with some code. 😄

Here’s the start of our helloMessage component:

<aura:component>

    <aura:attribute name="message" type="String"/>

    <p>Hello! [ message goes here, soon ]</p>

</aura:component>

The helloMessage component has one component attribute, and that attribute is defined by setting the name and type of the attribute. The name of the attribute is message and, once we learn about expressions, that’s how you’ll reference it. It still only outputs static text and HTML, but we’re inching closer to something useful.

👍 ?

The other attribute we’ve used here is type, and we’ve set it because it’s required in an attribute definition. It says that the message attribute contains a string, which makes sense. We’ll talk more about attribute data types, and the other parts of attribute definitions, but first let’s learn about expressions, and make helloMessage actually do something.

Expressions

Instead of getting lost in words again, let’s dive right into making helloMessage work as intended.

<aura:component>

    <aura:attribute name="message" type="String"/>

    <p>Hello! {!v.message}</p>

</aura:component>

Is that anticlimactic or what?

We’re outputting the contents of message using the expression {!v.message}. That is, this expression references the message attribute. The expression is evaluated, and resolves to the text string that’s currently stored in message. And that’s what the expression outputs into the body of the component.

Ummm…what the heck is an “expression”?

An expression is basically a formula, or a calculation, which you place within expression delimiters (“{!” and “}”). So, expressions look like the following:

{!<expression>}

The formal definition of an expression is a bit intimidating, but let’s look at and then unpack it: An expression is any set of literal values, variables, sub-expressions, or operators that can be resolved to a single value.

Yep, basically a formula, much like you’d write in a calculation field, filter criteria, or Visualforce. The formula, or expression, can contain a variety of things. Literal values should be obvious; they’re things like the number 42, or the string “Hello”. Variables are things like the message attribute. Operators are things like +, -, and so on, and sub-expressions basically means you can use parenthesis to group things together.

Let’s try this out, by making our expression slightly more complex.

<aura:component>

    <aura:attribute name="message" type="String"/>

    <p>{!'Hello! ' + v.message}</p>

</aura:component>

All we’ve done is move the “Hello” part from static text outside the expression to literal text inside the expression. Notice that we’ve used the “+” operator to concatenate the two strings together. This might seem like a pretty small difference, but moving the greeting text inside the expression lets you use labels, instead of literal text, which makes it easier to update (and translate) your components. For example:

{!$Label.c.Greeting + v.message}

Did you notice what our formal definition of expressions left out? JavaScript function calls. You can’t use JavaScript in expressions in Lightning Components markup.

One last thing about expressions before we move on. You can pass them to another component to set the value on that component. Here’s a new component that passes in a custom value to the helloMessage component. Passing the value to the other component overrides the value on that component.
<aura:component>
    <aura:attribute name="customMessage" type="String"/>
    <p> <c:helloMessage message="{!v.customMessage}"/> </p>
</aura:component>
.

Value Providers

Actually, we need to talk about another aspect of expressions. In the preceding examples, we’ve referenced the helloMessage component’s message attribute with v.message. What’s the “v.” part?

v is something called a value provider. Value providers are a way to group, encapsulate, and access related data. Value providers are a complicated topic, so for now, think of v as an automatic variable that’s made available for you to use. In our component, v is a value provider for the view, which is the helloMessage component itself.

v gives you a “hook” to access the component’s message attribute, and it’s how you access all of a component’s attributes.

Values in a value provider are accessed as named properties. To use a value, separate the value provider and the property name with a dot (period). For example, v.message, as we’ve seen.

When an attribute of a component is an object or other structured data (that is, not a primitive value), access the values on that attribute using the same dot notation. For example, {!v.account.Id} accesses the Id field of an account record. For deeply nested objects and attributes, continue adding dots to traverse the structure and access the nested values.

Attribute Data Types

Accessing structured data is a nice segue back to talking about attributes, and specifically about non-primitive attribute types. message is a string, but there are a number of different attribute types.

  • Primitives data types, such as Boolean, Date, DateTime, Decimal, Double, Integer, Long, or String. The usual suspects in any programming language.
  • Standard and custom Salesforce objects, such as Account or MyCustomObject__c.
  • Collections, such as List, Map, and Set.
  • Custom Apex classes.
  • Framework-specific types, such as Aura.Component, or Aura.Component[]. These are more advanced than we’ll get to in this module, but you should know they exist.

Here’s a stripped down look at the expenseItem component, which we’ll fill in later. It illustrates how you define an attribute for a custom object, and how to access fields on a record.

<aura:component>

    <aura:attribute name="expense" type="Expense__c"/>

    <p>Amount:
        <lightning:formattedNumber value="{!v.expense.Amount__c}" style="currency"/>
    </p>
    <p>
        Client: {!v.expense.Client__c}
    </p>
    <p>
        <lightning:input type="toggle"                            
                         label="Reimbursed?"                           
                         name="reimbursed"                         
                         checked="{!v.expense.Reimbursed__c}" />
     </p> 
    <!-- Other markup here -->

</aura:component>

This component has one attribute, expense, which is the custom object we created waaaay back at the start of this module. The component’s purpose is to display the details of an expense by referencing the field on the Expense__c record, using the {!v.expense.fieldName} expression. We’re using the <lightning:input> component of type="toggle", which is a checkbox in the form of a toggle, so that we can update the value in the UI later.

Other Aspects of Attribute Definitions

When it comes to the attributes you set on the <aura:attribute> tag, here’s the rest of what you need to know.

  • The default attribute defines the default attribute value. It’s used when the attribute is referenced and you haven’t yet set the attribute’s value.
  • The required attribute defines whether the attribute is required. The default is false.
  • The description attribute defines a brief summary of the attribute and its usage.

Setting the default value for an attribute with a complex data type can be kind of tricky. We’ll see an example later, though, so for now we’ll just give you the heads up.

Fun with Attributes and Expressions

To illustrate a few more concepts about attributes and expressions, let’s create a really silly component, helloPlayground, with the following markup.

<aura:component>

    <aura:attribute name="messages" type="List"
        default="['You look nice today.',
            'Great weather we\'re having.',
            'How are you?']"/>


    <h1>Hello Playground</h1>

    <p>Silly fun with attributes and expressions.</p>


    <h2>List Items</h2>

    <p><c:helloMessage message="{!v.messages[0]}"/></p>
    <p><c:helloMessage message="{!v.messages[1]}"/></p>
    <p><c:helloMessage message="{!v.messages[2]}"/></p>


    <h2>List Iteration</h2>

    <aura:iteration items="{!v.messages}" var="msg">
        <p><c:helloMessage message="{!msg}"/></p>
    </aura:iteration>


    <h2>Conditional Expressions and Global Value Providers</h2>

    <aura:if isTrue="{!$Browser.isIPhone}">
        <p><c:helloMessage message="{!v.messages[0]}"/></p>
    <aura:set attribute="else">
        <p><c:helloMessage message="{!v.messages[1]}"/></p>
        </aura:set>
    </aura:if>

</aura:component>

Now add the helloPlayground component to your harness app, and see how it runs!

There’s a number of new things here. We won’t go into depth on them right now, but you’ll see all of these again.

First, helloPlayground has one attribute, messages, that’s a complex data type, List. And it has a default value for that list, an array of three single-quoted strings separated by commas. And, in the List Items section, you can see how to access each of the strings using an index.

What happens if someone creates a <c:helloPlayground> with only two messages? Accessing the third item will fail, and while it won’t cause a crash here, with more complex components it might.

So, in the List Iteration section, you can see a better way to work through all items in the list. The <aura:iteration> component repeats its body once per item in its items attribute, so the list shrinks or grows as we have fewer or more messages.

In the Conditional Expressions and Global Value Providers section, you can see a way to choose between two different possible outputs. The format is a bit awkward, because this is markup rather than, say, JavaScript, but the <aura:if> component lets you, for example, add an edit button to a page only if the user has edit privileges on the object.

Finally, something that is a little less obvious. In object-oriented programming, there’s a difference between a class and an instance of that class. Components have a similar concept. When you create a .cmp resource, you are providing the definition (class) of that component. When you put a component tag in a .cmp, you are creating a reference to (instance of) that component.

It shouldn't be surprising that we can add multiple instances of the same component with different attributes. In the preceding example, when you use the default value for messages, you’ll end up with eight references to (instances of) our <c:helloMessage> component. If you pass in a longer list, you could end up with (many) more. All from our one little component!

And with that, friend, we think you’re ready for some real work.

Resources

Lightning bolt icon used to indicate that the content is for Lightning Experience

Remember, this module is meant for Lightning Experience. When you launch your hands-on org, switch to Lightning Experience to complete this challenge.

retargeting