Quickly Assemble Components Using Lightning Component Composition

Learn how to prebuilt Lightning components to quickly build (or compose) new components or apps.

Guest post: Peter Knolle is a Solutions Architect at Trifecta Technologies, Force.com MVP, and Salesforce Developer Group Organizer.  He holds six Salesforce certifications, blogs regularly at peterknolle.com, and is active on Twitter @PeterKnolle.

Lightning Components includes a framework (The Lightning Component framework) that provides different ways for components to be used together to create meaningful applications. One of those ways is through component composition, which is when one component contains multiple instances of other finer-grained components.  Instead of just starting from scratch on a new component or app, imagine that you can choose from a variety of different pre-built components and compose them together to build your new component or app.  Composing components together to make other components or applications is at the core of the Lightning Component framework and is a very powerful concept.

Lightning Component Composition

Let’s explore component composition by looking at how a Lightning Component, caseList, that lists cases for a user might be built.

In looking at the component output you can see that there are many different pieces that make it up. It looks like there is a piece that lists all of the cases, a piece that lists an individual case, and a piece that controls pagination. Now instead of the word “piece,” use the word component. The coarse-grained caseList component composes together other components with the result being a useful higher level component. So, how is it done? Let’s see how we can start with some basic component markup for the caseList component and add more component pieces to it to get the final component.

<aura:component controller="CaseController">
    <aura:attribute name="items" type="Case[]"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

    <div class="caseList">
        <aura:iteration items="{!v.items}" var="item">
            <c:casePanel case="{!item}"/>
        </aura:iteration>
    </div>

</aura:component>

The caseList component composes together an instance of an aura:iteration component with a custom casePanel component to output each case, showing specific information about the case in a formatted panel. The caseList component sets the attributes of the aura:iteration component and the casePanel component. With just a few lines of code we were able to compose together two pre-existing, unrelated components to provide another meaningful component. Pretty cool! Just wait. It gets better.

Now we’ve got a nice listing of cases, but what if there are too many to display at once? One way to deal with that is to add in a way to paginate.

<aura:component controller="CaseController">
    <aura:attribute name="items" type="Case[]"/>
    <aura:attribute name="numTotalItems" type="Integer"/>
    <aura:attribute name="pageSize" type="Integer" default="5"/>

    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

    <div class="caseList">
        <c:paginator items="{!v.items}" 
                     resultSize="{!v.numTotalItems}" 
                     pageSize="{!v.pageSize}" 
                     page="{!c.handlePage}">
                <aura:iteration items="{!v.items}" var="item">
                    <c:casePanel case="{!item}"/>
                </aura:iteration>
        </c:paginator>
    </div>

</aura:component>

We’ve introduced two new attributes: one to allow the user to specify the number of records to show at a time, pageSize, and the other, numTotalItems, is used internally to store the total number of records available. The caseList component usage has still remained simple. It can still be used as <c:caseList/> and can now also have a page size specified, <c:caseList pageSize=”8″/>.

The caseList component body has changed to compose the aura:iteration component with an instance of a custom, but already built, paginator component by wrapping the paginator around the aura:iteration component. When any markup is contained inside of the body of an instance of a component, that markup is set as the body attribute of the instance. Inside the implementation of the paginator component the body attribute can be referenced just as any other attribute through the “v” value provider (e.g., {!v.body}). The paginator‘s body attribute is set to the aura:iteration component. The paginator fires an event on pagination which the component handles with its handlePage method by retrieving the new set of records into the items attribute.

Lightning Component Facets

The caseList component looks nice so far, but it is still not quite there. It doesn’t have a title or other information about it. Let’s add that in.

<aura:component controller="CaseController">
    <aura:attribute name="items" type="Case[]"/>
    <aura:attribute name="numTotalItems" type="Integer"/>
    <aura:attribute name="pageSize" type="Integer" default="5"/>

    <aura:attribute name="title" type="String"/>
    <aura:attribute name="header" type="Aura.Component[]"/>
    <aura:attribute name="footer" type="Aura.Component[]"/>

    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

    <h1>{!v.title}</h1>
    <div>{!v.header}</div>

    <div class="caseList">
        <c:paginator items="{!v.items}" 
                     resultSize="{!v.numTotalItems}" 
                     pageSize="{!v.pageSize}" 
                     page="{!c.handlePage}">
                <aura:iteration items="{!v.items}" var="item">
                    <c:caseLine case="{!item}"/>
                </aura:iteration>
        </c:paginator>
    </div>

    <div>{!v.footer}</div>
</aura:component>

The caseList component now has a String title attribute, and a header and footer attribute, both of which have a type of Aura.Component[]. The title attribute is fairly straightforward, but the header and footer attributes are interesting. They are referred to as component facets. Any attribute of a component that has a type of Aura.Component[] is a facet. The user of the component can set the facet to any value they want using an aura:set. The caseList can now be used as follows to set an image, and a footer to always display.

<c:caseList pageSize="3" title="MY CASES">
    <aura:set attribute="header">
        <img src="/img/icon/cases32.png" alt="case icon"/>
    </aura:set>

    <aura:set attribute="footer">
        <ui:outputPhone value="1-800-555-HELP"/> or 
        <ui:outputEmail value="abc@example.com"/>
    </aura:set>
</c:caseList>

The header and footer facets are custom facets. Each component also has a special facet called the body facet. The body of an instance of a component is any free markup. For example, the paginator’s body is the aura:iteration component. The body attribute is not declared in each component, but rather is inherited from the root level aura:component. Technically, the free markup of a component instance body can be surrounded with aura:set attribute=”body,” but it is not necessary as the framework just treats all free markup in an instance as the body attribute.

It is easy to see how powerful component composition can be. We were able to build a fairly useful component very quickly by composing together a few pre-existing standard and custom components.

More

For more information on composition in the Lightning Component Framework, refer to the Lightning Component Developer’s Guide. For a quick overview and introduction to Lightning Components, visit the Lightning Components page on Salesforce Developers. If you’re ready to learn hands-on while earning badges to demonstrate your skills, start the Lightning Components module in Trailhead.

Leave your comments...

Quickly Assemble Components Using Lightning Component Composition