Tuesday, July 17, 2018

Salesforce Lightning Events Part 4 - Understanding Container Components



Till now we have learned about component events, bubble and capture phase in component events. Now before moving to the application events, there is one more important concept regarding events which we need to learn and that is Container Components.

What is Container Component ?

Container Components are the components that are not the direct owner/parent of any other component but contain another component between their tags wherever they are defined. For ex:- If I make two lightning components named:- one.cmp and two.cmp and instead of calling two.cmp inside one.cmp I do something like this:-
<c:one.cmp>
                      <c:two.cmp />
</c:one.cmp>
So, in the above case, one.cmp is my container component as it is containing two.cmp within it's  opening and closing tags and is not the parent or direct owner of two.cmp.

While learning about component events bubble and capture phase I have told about hierarchy or containment hierarchy. So, let's see what a container component is and how this hierarchy is implemented. I'll be using the previous code implemented till now so if you just joined or want access to the current implementation you can read my previous blogs in the series listed here. Or at least have a look at the bubble phase implementation here.

So, we have 2 components already implemented:- LightningEventsCompContainer and LightningEventsComp1. Now, I am going to add one more component i.e. LightningEventsCompWrapper. This component is basically a container component which will be wrapping our base component i.e. LightningEventsComp1 and will be a direct child of LightningEventsCompContainer. Let's jump on to the code now for a deeper understanding:-

LightningEventsCompContainer.cmp

We have already used this component as it is the parent of our source component and is also responsible for handling the event fired by the source component. However, I have made a simple change in this component which you can see below:- 

<!-- Wrapper/Parent component called directly from the Lightning Application -->
<aura:component>
        <!-- Attribute to store total income coming through the event -->
        <aura:attribute name="totalIncome" type="decimal" default="0" ></aura:attribute>
        <!-- Handler defined to handle 'totalIncomeComponentEvent' name same as used in registerEvent tag -->
        <aura:handler name="totalIncomeComponentEvent" event="c:LightningComponentEvent"  action="{!c.handleTotalIncomeComponentEvent}"></aura:handler>
        Outer Component
        <!-- Inner component section with border -->
        <div class="innerComponent">
                Inner Component Section Begin
                <!-- Calling the inner component container -->
                <c:LightningEventsCompWrapper>
                        <!-- Calling the source component -->
                        <c:LightningEventsComp1 />
                </c:LightningEventsCompWrapper>
                Inner Component Section End
        </div>
        <!-- Section to show total income -->
        <span class="totalIncome">Total Income = {!v.totalIncome}</span>
</aura:component>
As you can see, I have wrapped the LightningEventsComp1 component in another lightning component named LightningEventsCompWrapper i.e. the tags of the wrapper component are now surrounding my source component. In terms of Salesforce, this component which is surrounding the source component in it's tags is called Container Component but in this tutorial, we'll call it as wrapper component for simplicity and as we have already made a component named LightningEventsCompContainer which is containing everything and is the parent component. So, let's move to this wrapper component now and see what we have in it.

LightningEventsCompWrapper.cmp

Below is the code for our new container component for innermost component which is firing the event. As it is wrapping my source component, I have named it as wrapper but in Salesforce docs, such a component is called as container component as it is containing another component within it's tags and is not the direct owner:- 

<!-- Container to wrap inner component -->
<aura:component>
        <!-- Event handler in wrapper component -->
        <aura:handler name="totalIncomeComponentEvent" event="c:LightningComponentEvent"  action="{!c.handleTotalIncomeComponentEvent}" includeFacets="true"></aura:handler>
        <!-- Wrapper Component -->
        <div class="wrapperComponent">
                Wrapper Component Section Begin
                <!-- Calling the body i.e. the data between the tags of wrapper/container component -->
                {!v.body}
                Wrapper Component Section End
        </div>
</aura:component>
As you can see above, in this component, I have defined a handler which is the same as we have defined in our parent component i.e. LightningEventsCompContainer. This handler is simply responsible for handling the event fired by our source component and is calling the controller function named:- handleTotalIncomeComponentEvent. Below this handler we have defined a div with class wrapperComponent which is responsible for giving a red border to this wrapper component and in the body of the div, I have called {!v.body} which is mainly responsible to get all the data between the tags of this component to this location inside our component as you can see, we have <c:LightningEventsComp1 /> inside the tags <c:LightningEventsCompWrapper> and </c:LightningEventsCompWrapper> in LightningEventsCompContainer.cmp above. So, here {!v.body} will be replaced by <c:LightningEventsComp1 /> and our source component will be inside the Inner Component section Begin and End lines as it takes the position of v.body.

There is one more thing to notice i.e. I have included an extra attribute named includeFacets="true" in the component event handler defined in wrapper component. This is because generally our wrapper component is only containing the source component in it's tags and therefore it'll not be able to handle the event fired by our source component, as it is not the direct owner/parent. So, to make it able to handle the events, we have to include includeFacets="true" attribute in the handler defined in our wrapper component.

LightningEventsCompWrapperController.js

({
        // Function invoked when event is handled
        handleTotalIncomeComponentEvent : function(component, event, helper) {
                alert('Event handler at the wrapper component');
        }
})

As you can see in the above code, I have simply defined a function named handleTotalIncomeComponentEvent which will be called when my event is handled by the wrapper component and it is simply giving an alert with message:- Event handler at the wrapper component so that, we know that the handler at the wrapper component is handling the event.

LightningEventsCompWrapper.css

Below is the css file that is defining the wrapperComponent class used in div to give a red border to our wrapper component in order to differentiate it with other components in the hierarchy.

.THIS {
}
/* Border of wrapper component */
.THIS.wrapperComponent {
        border: 1px solid red;
}
So, we have given the border to our wrapper component using border: 1px solid red; in the wrapperComponent class which is used  by our div.

I have configured all the handlers to work in bubble phase i.e. without having phase="capture" attribute in them and when you run this in your browser and click on Calculate Total Income button, you get the following outputs:-

Event handler at the source component is fired

Event handler at the wrapper component is fired

Event handler at the parent component is fired

No comments:

Post a Comment