How Variable Types Operate in the Lightning Component Framework

When writing a component or application, you can set an attribute to have a specific type. But how is the type being used by Aura, the underlying framework of Lightning Components? What benefits do you get from choosing one type over another? What pitfalls do we have to look out for? Lastly, how does the selection of these types impact how the data can be communicated to and from the server?

Choosing the right type for the job is a core decision when building an API, so let’s take a look under the hood.

In this article, we are going to look at how components operate with four different kinds of variable types and why it is important to make them a distinct type. This article assumes you have a working knowledge of using Lightning Components. If you are new to Lightning Components, running through the Lightning Basics Module on Trailhead would be a good start.

Specifying the type of your attributes

When specifying your attributes, it is a good thing to remember that even though you write your components in JavaScript and markup files, the Aura Framework is written in Java. Thus metadata about your component, such as its attribute types are going to map back to something the Java backend of Aura can understand.

When it comes to choosing a specific type for your component, it’s particularly important to understand that the type is related to a Java definition of the type, not JavaScript. So Object here refers to java.lang.object, not JavaScript’s window.Object, String relates to java.lang.String, Map to java.util.Map, etc.

Let’s start on the client. An attribute is defined by two properties name and type:

<!-- myCmp.cmp -->
<aura:attribute name="myString" type="String"/>
<aura:attribute name="myObject" type="Object"/>
<aura:attribute name="myMap" type="Map"/>
<aura:attribute name="myFacet" type="Aura.Component[]"/>

Let’s review each attribute individually:

myString attribute

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

Your basic String attribute. Nothing fancy.

myObject attribute

<aura:attribute name="myObject" type="Object"/>

As we said earlier, the Types are mapping back to Java Types. So this is specifying that the value in this attribute is of type java.lang.Object, which is potentially problematic. While the Aura Framework client will not be affected, on the server the type Object might not get converted to the correct data type we expect.

On the server, we’ll use something called the Aura Converter Service to convert values from one type to another. In this case with type="Object" it will ignore any conversion for this attribute, possibly resulting in the incorrect data type being present. This example might compile and run initially, but could easily break down the road. If using a generic base type is desired, avoid Object as it means different things to JavaScript as it does to our Java Services. So instead of Object, use Map.

We’ll give further examples of how this can fail further in this article.

myMap attribute

<aura:attribute name="myMap" type="Map"/>

Here we’re using Map, which will properly handle data conversions from client and server.
A key value store that will be an instance of window.Object, not an actual JavaScript Map.

myFacet attribute

<aura:attribute name="myFacet" type="Aura.Component[]"/>

Aura.Component[] is a special type to the framework indicating that the contents should be treated as Components. It is the only type of MetaData that the Aura Framework Client Runtime references to make decisions on how to process your component.

Check out this doc on Attribute Types for more info.

Some pitfalls

type=”Object” is bad
As we’ll talk more about, type="Object" is not what you expect it to be. It is the source of many different bugs, and you’ll want to avoid it as much as possible. Most often, what you really wanted was type="Map".

Always specify Aura.Component[] as an array
Due to framework limitations, declaring a type of Aura.Component will not work as expected. Always specify it as Aura.Component[].

No Set or Get validation
There is NO validation on what gets put into or comes out of an attribute. Any errors you get will be downstream for code that got an unexpected datatype.

Client usage of types

When you get or set a type, it can be anything.

component.set(“v.myFacet”, “Parker Harris”);
component.set(“v.myMap”, Date.now());
component.get("v.myFacet");

There is nothing in getting or setting an attribute in Aura framework that is going to complain about the data types.

You will need to still be aware though of how these attributes are used. For example, v.myFacet is being rendered to the page in an expression {!v.myFacet}. Since you’ve updated it to a new value, the framework is going to try to output that to the page. Since you put a String in v.myFacet instead of a Component, the rendering service for Aura is going to likely throw some exceptions.

Default values

Types are used in determining the type of our default values. This is another place where type="Object" can bite you.

Let’s look at our myCmp component with all its attributes populated with default values.

<!-- myCmp.cmp -->
<aura:component>
    <aura:attribute name="myFacet" type="Aura.Component[]">
        <div>A Div</div>
    </aura:attribute>
    <aura:attribute name="myObject" type="Object" default='{"blog":true}'/>
    <aura:attribute name="myMap" type="Map" default='{"blog":true}'/>
    <aura:attribute name="myString" type="String" default="42"/>
    
    <aura:handler name="init" value="{!this}" action="{!c.init}"/>
</aura:component>

Here we’ve specified values for each of the attributes. Let’s see what the component actually gets created with.

<!-- myCmpController.js -->
({
    init: function(component, event, helper) {
        console.log(Array.isArray(component.get("v.myFacet"))); // true
        console.log(typeof component.get("v.myObject")); // string
        console.log(typeof component.get("v.myMap")); // object
        console.log(typeof component.get("v.myString")); // string
    }
})

As you can see, because of type="Object" we ended up with the wrong data type in v.myObject. It is not a JavaScript object because a Java String is an instance of Java Object, and so we did no conversion on it at the server level.

v.myMap did get converted correctly since we knew on the server that we wanted it to be a Java Map, which then gets converted to a proper JavaScript object.

The other attributes all have the correct data sources.

Server component creation

While Apex and Lightning Data Service have types, the attribute types are not accounted for in those situations. They only come into play on the server when creating instances of your component. This is a feature not directly accessible to Lightning Component Framework developers. BUT it is still something you should concern yourself with as its possible to cause a great many exceptions and not understand why.

The three ways to do server side component creation are

  1. Applications
  2. $A.createComponent
  3. Embed your component in a layout.

Applications

When the framework builds an application to show to the user, be it your own custom application or the Salesforce Lightning Experience, that is a form of Server Component Creation. Underneath the covers, we are creating an Aura.Component instance on the server in the shape of your application, loading in all the attributes and figuring out what the output of the page should be.

When you have an application with attributes, they can be specified on the URL with query parameters for an application.

<!-- myApp.app -->
<aura:application >
    <aura:attribute name="myFacet" type="Aura.Component[]"/>
    <aura:attribute name="myObject" type="Object"/>
    <aura:attribute name="myMap" type="Map"/>
    <aura:attribute name="myString" type="String" default="Blank"/>
    
    <aura:handler name="init" value="{!this}" action="{!c.init}"/>
    
    {!v.myString}
</aura:application>

For example, accessing /c/myApp.app?myString=Blog will output a page with a single word on it “Blog”.

Now let’s look at what happens if you specify myObject and myMap on the URL:

/c/myApp.app?myObject={"blog":true}&myMap={"blog":true}

Here we are specifying the values for these attributes as JSON, which will get converted to. If we check their values in the controller though, here’s what we find.

({
    init : function(component, event, helper) {
        console.log(typeof component.get("v.myObject")); // String
        console.log(typeof component.get("v.myMap")); // Object
    }
})

Why are these different?

Because when we created the application on the server, we parsed out these values from the URL. Since these values start as strings, we have to convert them to the specified datatypes in your attributes. For myObject, we checked to see if it was a java.lang.Object, since strings are objects (everything is), we left without converting it. For myMap, we tested to see if it was a java.util.Map and since it wasn’t, we used a StringToMap converter on the server.

$A.createComponent

Server component creation should be a transparent operation to the user. Whether the component gets created right away or has to wait till it can go to the server shouldn’t matter.

Yet, there are a few reasons to know that it can go to the server. Primarily that we’re going to run data type conversions if we do go to the server.

Let’s create an instance of our previously defined component myCmp.cmp

<!-- myAppController.js -->
({
    init : function(component, event, helper) {
        $A.createComponent("c:myCmp", {
            myMap: 42
        }, function(cmp, state) {
            console.log(state); // SUCCESS
        })
    }
})

This code will go to the server when we do not have all the code necessary to create an instance of the component requested.

On the server, in the process of figuring out what to send back, we create an instance of the requested component. As soon as that happens, we do data conversions on the specified parameter values and that’s when an “Unexpected Exception Occurred” can happen.

Regardless of going to the server or not, if you set myFacet to 42 instead of myMap, you’ll notice the client does complain regardless on if markup:// is there or not. That again is because myFacet is of type Aura.Component[] and those attributes types are special and leveraged during the life cycle stages of the Aura framework.

Check out further documentation on $A.createComponent.

Embedding your component in a layout

As you may know, implementing the interface flexipage:availableForAllPageTypes will allow you to add your component to differing layouts in the application.

Additionally, adding interfaces such as force:hasRecordId will add an attribute that will be set on the server by special Salesforce components. In this case, you again have Server Component Creation. The scope of challenges here are many and deserve their own blog post. Just beware to use the right data types and it should avoid pitfalls.

Conclusion

Takeaways

  • No validation happens on the client to validate types. (Set anything you want, get could be anything)
  • Types are leveraged mostly on the server, particularly in default values and Server Component Creation via apps or $A.createComponent.
  • type="Object" can have unfortunate consequences in Server Component Creation (also effects testing in Aura) so use type="Map" instead.
  • With Aura.Component[] you can specify default values in attributes to be more components.

That’s it for how types are translated between client and server in Lightning Components. Obviously, component code can do type checking, but there’s nothing in the framework APIs such as component.set() or event.setParams() that cares what the type of the parameter is versus what you passed in. Choosing the right data types helps prevent bugs with server attribute serialization. So choose wisely and avoid a type of Object. If you feel that urge, then use Map instead.

Related Trailhead modules

About the author

Kris Gray is a Principal Developer at Salesforce.com. He’s worked on the underlying framework for Salesforce for over nine years including the Classic Skin, Inline Edit, the Lightning Framework, and Visualforce Integration in Lightning. He also manages the Lightning Inspector and is currently a member of the Aura Framework and Lightning Tooling teams.

You can follow him on Twitter at @GrayJustise

Leave your comments...

How Variable Types Operate in the Lightning Component Framework