Friday, June 7, 2019

lightning/uiObjectInfoApi Example (record id and object api fetching)

1. Example to fetch record Id and object API.

recordIdFetching.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="recordIdFetching">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>
    
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>   
    </targets>
</LightningComponentBundle>


recordIdFetching.html
<template>
    <lightning-card title="Record Id Fetching">
        <lightning-layout>
            <lightning-layout-item>
                Record Id : {recordId} <br/>
                Objcet API : {objectApiName}
            </lightning-layout-item>
        </lightning-layout>
    </lightning-card>
</template>


recordIdFetching.js

import { LightningElement, api } from 'lwc';

export default class RecordIdFetching extends LightningElement {
    @api recordId;
    @api objectApiName;
}


Finally deploy it in our auth org and keep in mind to configure this LWC component under any lighting record page.
Once done open the record where you configure the above component using lighting record page.

Output:
It will shown record id and object api name.

Lightning Data Service in LWC

Below are 3 base Lightning components built on Lightning Data Service (LDS),

1. lightning-record-form
2. lightning-record-view-form
3. lightning-record-edit-form

Note:
It is always suggested to use above 3 components and if the above 3 components don't give enough flexibility, then use a wire adapter like getRecord and getFieldValue(record, field).

1. lightning-record-form

Using <lightning-record-form> we can create quick form to create, modify and view a record. There is no need to create manually like other 2 (lightning-record-edit-form and lightning-record-view-form)

Limitations:
Does not support client side validation.
All standard objects are not supported like task, event.

Code format:

<lightning-record-form
 record-id="Id of the record which you want to use"
 object-api-name="API name of the object which you want to use"
 layout-type="Full / Compact"
 fields="Fields you want to display."
 columns="Number of column you want to display"
 mode="view / edit / readonly">
</lightning-record-form>

Mode Details ==>
In view mode there will be a edit icon in each field and save, cancel button for editing is available,
but while mode is readonly then field will be shown as output field.
If mode is edit then UI will shown edit form with save and cancel button and after save the content it will return to view mode.

Mode default value is View.


===================================================================
A sample example of <lightning-record-form>. We can dynamically fetch the record id, but for simplicity I am taking hard code Account Id for this example.


Note: Please change the account id value as per your record.
===================================================================

recordForm.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="recordForm">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>
    
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>   
    </targets>
</LightningComponentBundle>


recordForm.html
<template>
        <lightning-card title="Account Details">
                <lightning-layout>
                    <lightning-layout-item>
                        <lightning-record-form
                            record-id="0017F00GHHG01b7ll2QAA"
                            object-api-name="Account"
                            fields={selectedFields}
                            columns="2"
                            mode="edit">
                        </lightning-record-form>
                    </lightning-layout-item>
                </lightning-layout>
        </lightning-card>
</template>


recordForm.js
import { LightningElement } from 'lwc';

//import ACCOUNT_ID from '@salesforce/account/Id';
import ACCOUNT_NAME from '@salesforce/schema/Account.Name';
import ACCOUNT_TYPE from '@salesforce/schema/Account.Type';
import ACCOUNT_OWNER from '@salesforce/schema/Account.OwnerId';

export default class RecordForm extends LightningElement {

    selectedFields = [ACCOUNT_NAME, ACCOUNT_TYPE, ACCOUNT_OWNER];
}


Output:



2. lightning-record-view-form
===================================================================
A sample example of <lightning-record-view-form>. We can dynamically fetch the record id, but for simplicity I am taking hard code Account Id for this example.

Note: Please change the account id value as per your record.
===================================================================

recordViewForm.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="recordViewForm">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>
    
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>   
    </targets>
</LightningComponentBundle>


recordViewForm.html
<template>
        <lightning-card title="Account Details Record View Form">
                <lightning-layout>
                    <lightning-layout-item>
                        <lightning-record-view-form
                            record-id="0017F000HGFH7ll2QAA"
                            object-api-name="Account">
                            <lightning-output-field field-name="Name"></lightning-output-field>
                            <lightning-output-field field-name="Type"></lightning-output-field>
                            <lightning-output-field field-name="OwnerId"></lightning-output-field>
                            <lightning-output-field field-name="LastModifiedById"></lightning-output-field>
                        </lightning-record-view-form>
                    </lightning-layout-item>
                </lightning-layout>
        </lightning-card>
</template>


Output:






















3. lightning-record-edit-form
===================================================================
A sample example of <lightning-record-edit-form>. We can dynamically fetch the record id, but for simplicity I am taking hard code Account Id for this example.
===================================================================

recordEditForm.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="recordEditForm">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>
    
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>   
    </targets>
</LightningComponentBundle>


recordEditForm.html
<template>
        <lightning-card title="Account Details Record Edit Form ..">
            <lightning-layout>
                <lightning-layout-item>
                    <lightning-record-edit-form
                    object-api-name="Account"
                    onsuccess={handleSuccess}>
                        <!-- We can use this simple version like designing of indivisual input field -->
                        <lightning-input-field field-name={accountName}></lightning-input-field> 
                        <lightning-input-field field-name={accountType}></lightning-input-field>

                        <!-- Or we can use the other version like designing multiple input fields using for loop. -->
                        <!--<template if:true={selectedFields}>
                            <template for:each={selectedFields} for:item="field">
                                <div key={field}>
                                    <lightning-input-field field-name={field}></lightning-input-field>
                                </div>
                            </template>
                        </template>-->

                        <lightning-button variant="brand" type="submit" name="save" label="Save"></lightning-button>
                    </lightning-record-edit-form>
                </lightning-layout-item>
            </lightning-layout>
        </lightning-card>
</template>


recordEditForm.js

import { LightningElement } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import ACCOUNT_NAME from '@salesforce/schema/Account.Name';
import ACCOUNT_TYPE from '@salesforce/schema/Account.Type';

export default class RecordEditForm extends LightningElement {
    accountName = ACCOUNT_NAME;
    accountType = ACCOUNT_TYPE;
    
    //selectedFields = [ACCOUNT_NAME, ACCOUNT_TYPE];
        
    handleSuccess(){
        if(this.recordId !== null){
            this.dispatchEvent(new ShowToastEvent({
                    title: "SUCCESS!",
                    message: "New record has been created.",
                    variant: "success",
                }),
             );
                
        }
    }
}


Output:



















LDS is built on top of User Interface API (UI API). UI API is a public Salesforce API that Salesforce uses to build Lightning Experience and Salesforce for iOS and Android.
We can build Salesforce UI very easy using UI API.

LDS structure,






















1. Create Lightning Web Component using LDS.
2. LDS use UI API to connect Database.

Response from UI API supports CURD access, FLS, sharing settings. To enjoy these features we have to use LWS wire adapters (lightning/ui*Api).

Here we use TypeScript formatting and it's format is as follows,

function name, parameter name and type, and a return type (Optional parameters are appended with ?).

createRecord(recordInput: Record, originalRecord?: Record): Record


************************************************
Please check my next post for lightning/uiObjectInfoApi and lightning/uiRecordApi examples.
************************************************

Monday, May 20, 2019

Import and Export in LWC.

Import and Export is very powerful feature which we will use in LWC. We know the word re-usability and generally we always try to use re-usable component for smart coding.
Here we will use ES6 module which is nothing but JS file that export variables or functions that other modules can use.
Basically to share our common code base between different components, we will create ES6 module and then export the variables or functions that we want to share or want to make as re-usable.

Scenario:
Say for our case we want to create a temperature calculator which will convert temperature from Celsius to Fahrenheit or vice versa. And we want to use this calculator from different components.

Solutions:
So, here we will create below 3 steps,

  • Create a JS file say temperatureCalculator.js and under this JS file, build our business logic like conversion of temperature from Celsius to Fahrenheit or vice versa.
  • Then export the function (where we implement our logic.).
  • Finally import the above re-usable component (temperatureDemo) from another component.
***************** Export Functionality *****************

temperatureCalculator.js
const convertCelsiusToFahrenheit = (c) => {
    return (9/5)*c + 32 ;
};

const convertFahrenheitToCelsius = (f) => {
    return 5/9 * (f-32);
};

export {
    convertCelsiusToFahrenheit,
    convertFahrenheitToCelsius
}


temperatureCalculator.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="temperatureCalculator">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>
    
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>   
    </targets>
</LightningComponentBundle>


***************** Import Functionality *****************

temperatureDemo.html
<template>
        <lightning-card title="Calculate Temperature">
            <lightning-layout>
                <lightning-layout-item>
                        <lightning-input type="number" label="Temperature" value={temp} onchange={fetchTemperature}></lightning-input>
                        <lightning-combobox label="Conversion Type" value={type} options={conversionType} onchange={fetchConversionType}></lightning-combobox>
                        <br/>
                        <lightning-button variant="brand" label="Calculate" onclick={calculateTemperature}></lightning-button>
                        {result}
                </lightning-layout-item>
            </lightning-layout>
        </lightning-card>
    </template>

temperatureDemo.js
import { LightningElement, track } from 'lwc';
import { convertCelsiusToFahrenheit } from 'c/temperatureCalculator';
import { convertFahrenheitToCelsius } from 'c/temperatureCalculator';

export default class TemperatureDemo extends LightningElement {
    @track type;
    @track temp;
    @track result;

    conversionType = [
        { label: "Celsius To Fahrenheit", value: "c2f" },
        { label: "Fahrenheit To Celsius", value: "f2c" }
    ];

    fetchTemperature(event){
        this.temp = event.target.value;
    }

    fetchConversionType(event){
        this.type = event.target.value;
    }

    calculateTemperature(){
        if(this.type === "c2f"){
            this.result = convertCelsiusToFahrenheit(this.temp);
        } else{
            this.result = convertFahrenheitToCelsius(this.temp);
        }                
    }
}

temperatureDemo.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="temperatureCalculator">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>
    
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>   
    </targets>
</LightningComponentBundle>

Now put this temperatureDemo LWC component in your Lightning Page and check the result.

Output:

Initial load:























After button click:

Publish Subscribe Pattern in LWC (Component Communication).

1. publish-subscribe pattern/model is same like Application Event in Lighting Component. If you want to communicate between components those are not bounding in parent child relationship, but available in same page (say, in same app-builder page), then we will use publish-subscribe pattern. Here Salesforce already publish a pubsub.js file which we will use. You can download pubsub.js file from below link or copy paste below pubsub.js.
https://github.com/trailheadapps/lwc-recipes/blob/master/force-app/main/default/lwc/pubsub/pubsub.js

Now create a LWC component named as pubsub and update the pubsub.js file with downloaded code base or copy paste below source code,

/**
 * A basic pub-sub mechanism for sibling component communication
 *
 * TODO - adopt standard flexipage sibling communication mechanism when it's available.
 */

const events = {};

const samePageRef = (pageRef1, pageRef2) => {
    const obj1 = pageRef1.attributes;
    const obj2 = pageRef2.attributes;
    return Object.keys(obj1)
        .concat(Object.keys(obj2))
        .every(key => {
            return obj1[key] === obj2[key];
        });
};

/**
 * Registers a callback for an event
 * @param {string} eventName - Name of the event to listen for.
 * @param {function} callback - Function to invoke when said event is fired.
 * @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
 */
const registerListener = (eventName, callback, thisArg) => {
    // Checking that the listener has a pageRef property. We rely on that property for filtering purpose in fireEvent()
    if (!thisArg.pageRef) {
        throw new Error(
            'pubsub listeners need a "@wire(CurrentPageReference) pageRef" property'
        );
    }

    if (!events[eventName]) {
        events[eventName] = [];
    }
    const duplicate = events[eventName].find(listener => {
        return listener.callback === callback && listener.thisArg === thisArg;
    });
    if (!duplicate) {
        events[eventName].push({ callback, thisArg });
    }
};

/**
 * Unregisters a callback for an event
 * @param {string} eventName - Name of the event to unregister from.
 * @param {function} callback - Function to unregister.
 * @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
 */
const unregisterListener = (eventName, callback, thisArg) => {
    if (events[eventName]) {
        events[eventName] = events[eventName].filter(
            listener =>
                listener.callback !== callback || listener.thisArg !== thisArg
        );
    }
};

/**
 * Unregisters all event listeners bound to an object.
 * @param {object} thisArg - All the callbacks bound to this object will be removed.
 */
const unregisterAllListeners = thisArg => {
    Object.keys(events).forEach(eventName => {
        events[eventName] = events[eventName].filter(
            listener => listener.thisArg !== thisArg
        );
    });
};

/**
 * Fires an event to listeners.
 * @param {object} pageRef - Reference of the page that represents the event scope.
 * @param {string} eventName - Name of the event to fire.
 * @param {*} payload - Payload of the event to fire.
 */
const fireEvent = (pageRef, eventName, payload) => {
    if (events[eventName]) {
        const listeners = events[eventName];
        listeners.forEach(listener => {
            if (samePageRef(pageRef, listener.thisArg.pageRef)) {
                try {
                    listener.callback.call(listener.thisArg, payload);
                } catch (error) {
                    // fail silently
                }
            }
        });
    }
};

export {
    registerListener,
    unregisterListener,
    unregisterAllListeners,
    fireEvent
};

2. Now we have to create two more components, one will be used to publish an event and another will be used to subscribe the event. Say, our publisher component name is myPublisher and our subscriber component is mySubscriber.

3.  ************* Publisher Component Details. *************

myPublisher.html
<template>
    <lightning-card title="Publisher Component">
        <lightning-layout>
            <lightning-layout-item>
                <button onclick={displayLabradorDetails}>Labrador</button>
            </lightning-layout-item>
        </lightning-layout>
    </lightning-card>
</template>

myPublisher.js
import { LightningElement, wire } from 'lwc';
import { CurrentPageReference } from 'lightning/navigation';
import { fireEvent } from 'c/pubsub';

export default class MyPublisher extends LightningElement {
    @wire(CurrentPageReference) pageRef;

    displayLabradorDetails(){
        fireEvent(this.pageRef, "eventdetails", "Breed - Labrador");
    }
}

myPublisher.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="myPublisher">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>

    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>   
    </targets>
</LightningComponentBundle>


4.  ************* Subscriber Component Details. *************

mySubscriber.html
<template>
    <lightning-card title="Subscriber Component">
        <lightning-layout>
            <lightning-layout-item>
                {details}
            </lightning-layout-item>
        </lightning-layout>
    </lightning-card>
</template>

mySubscriber.js
import { LightningElement, track, wire } from 'lwc';
import { registerListener, unregisterAllListeners } from 'c/pubsub';
import { CurrentPageReference } from 'lightning/navigation';

export default class MySubscriber extends LightningElement {
    @track details;
    @wire(CurrentPageReference) pageRef;

    connectedCallback() {
        registerListener("eventdetails", this.sutUpDetails, this);
    }
     
    disconnectedCallback() {
        unregisterAllListeners(this);
    }

    sutUpDetails(dogDtl){
        this.details = dogDtl;
    }
}

mySubscriber.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="mySubscriber">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>

    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>   
    </targets>
</LightningComponentBundle>

5. Now authorize your Org and then deploy all above 3 LWC components in your authorized Org and finally drag myPublisher and mySubscriber in your Lightning Page.

6. Output ==>

Initial Load:


















After click on button:

Saturday, May 18, 2019

Custom Event in LWC.

Custom Event in LWC is same like Component Event in Lightning Component. Communicate from parent to child component can be done using passing property value or calling method of child component from parent component.
But if the communication is reverse i.e. communication from child to parent component then we can use Custom Event.
Here the technique is different with respect to Lighting Component.

In child component JS file, we have to CREATE and DISPATCH the the event.
To create an event, use the CustomEvent() constructor. And then we use dispatchEvent() method to dispatch the event.

customEventChildComp.html
<template>
    <lightning-button label="Pass message from child component" onclick={handleCustomEvent}></lightning-button>
</template>

customEventChildComp.js
import { LightningElement } from 'lwc';

export default class CustomEventChildComp extends LightningElement {
    handleCustomEvent(){
        //Use the CustomEvent() constructor to create an event. And always use event name in lower case.
        const myCustomEvent = new CustomEvent("displaymessage", { detail: "Message Passing from child component using custom event." });

        //Use dispatchEvent() method to dispatch the above created event.
        this.dispatchEvent(myCustomEvent);
    }
}

Now we can listen event from parent in 2 ways,

1. Declaratively from the component’s HTML template :
2. Programmatically using an imperative JavaScript API :

Note: It’s better to listen from the HTML template since it reduces the amount of code you need to write.

customEventParentComp.html
<template>
    <lightning-card title="Custom Event Example.">
            <lightning-layout>
                <lightning-layout-item>
                    {messageFrmChild}<br/>
                    <!-- Declaratively listen custom event. Use 'on' prefix before event name declared in child component named as 'displaymessage' -->
                    <c-custom-event-child-comp ondisplaymessage={handelMessageFromChild}></c-custom-event-child-comp>
                </lightning-layout-item>
            </lightning-layout>
        </lightning-card>
</template>

customEventParentComp.js
import { LightningElement, api } from 'lwc';

export default class CustomEventParentComp extends LightningElement {
    @api
    messageFrmChild;
    handelMessageFromChild(event){
        this.messageFrmChild = event.detail;
    }
}

customEventParentCompjs-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="customEventParentComp">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>

    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>   
    </targets>
</LightningComponentBundle>


Then open any existing appBuilder / create a new one and drag the <parentComponent> in to respective column and then save and activate it.  Now click the tab to show it in browser and it will look as follows,

Output ==>
Before calling event













After calling event by button click

Getter Setter Method in LWC.

1. Create a LWC component named as "getterSetterExample". Where getterSetterExample.js has a getter and a setter method for "greetingMessage" and in "getterSetterExample.html" we will call "greetingMessage" message
to display.

getterSetterExample.html
<template>
    {greetingMessage}
</template>

getterSetterExample.js
import { LightningElement, api } from 'lwc';

export default class GetterSetterExample extends LightningElement {
    
    //Get method use to return greeting message.
    @api
    get greetingMessage(){
        return this._greetingMessage;
    }

    //Set method use to setup greeting message in upper case.
    set greetingMessage(value){
        this._greetingMessage = value.toUpperCase();
    }
}


2. Now call this "getterSetterExample" component from "useGetterSetter" component.

useGetterSetter.html
<template>
    <lightning-card title="Getter Setter Example">
        <lightning-layout>
            <lightning-layout-item>
                <!-- Passing greeting-message in small letter and expected result: message will be shown in capital letter. -->
                <c-getter-setter-example greeting-message="hello everyone. welcome to lwc getter setter example."></c-getter-setter-example>
            </lightning-layout-item>
        </lightning-layout>
    </lightning-card>
</template>

Now to make this useGetterSetter available in AppBuilder/Record Page/Home Page, please update "useGetterSetter.js-meta.xml" file as below,

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="useGetterSetter">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>

    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>   
    </targets>
</LightningComponentBundle>


3. Then open any existing appBuilder / create a new one and drag the <useGetterSetter> in to respective column and then save and activate it.  Now click the tab to show it in browser and it will look as follows,















4. So, we are passing value of "greeting-message" property of "getterSetterExample" LWC component from "useGetterSetter" LWC component, in small letter as "hello everyone. welcome to lwc getter setter example.".
Now this message will be set in upper case in setter method of "getterSetterExample.js" file and from getter method it will return the value.
So, while calling child component (getterSetterExample) from parent component (useGetterSetter) with above message (in small letter), then system will display the message in capital letter. And the output is as follows,


Calling child method form parent (Parent Child Communication).

1. childComponent.html

<template>
    {childComMessage}
</template>

2. childComponent.js
import { LightningElement, api } from 'lwc';

export default class TestMyLwcComp extends LightningElement {
    @api childComMessage;

    @api
    messageFromChild(){
        this.childComMessage = "Message set from child method...........";
    }
}

3. parentComponent.html

<template>
    <lightning-card title="Parent Component">
        <lightning-layout>
            <lightning-layout-item>
                <c-child-component></c-child-component>
                <button onclick={displayChildMessage}>Display Child Message</button>
            </lightning-layout-item>
        </lightning-layout>
    </lightning-card>
</template>

4. parentComponent.js

import { LightningElement } from 'lwc';

export default class ParentComp extends LightningElement {

    displayChildMessage(){
        this.template.querySelector('c-child-component').messageFromChild();
    }
}

5. Now to make this parentComponent available in AppBuilder/Record Page/Home Page, please update "parentComponent.js-meta.xml" file as below,

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="parentComponent">
    <apiVersion>45.0</apiVersion>
    <isExposed>true</isExposed>

    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__RecordPage</target>
        <target>lightning__HomePage</target>   
    </targets>
</LightningComponentBundle>

6. Now deploy it to your authenticated ORG. If you are not authenticated any ORG till now then click  [Ctrl + Shift + P] and in command pallet type "Authorize an Org" and select "Authorize an Org" and from browser authenticate your destination ORG where you want to deploy parent and child component.

7. Then open any existing appBuilder / create a new one and drag the <parentComponent> in to respective column and then save and activate it.  Now click the tab to show it in browser and it will look as follows,

Initial load















After click on <Display Child Message> button.

Data Cloud Part 6: Practical (Ingest Physical Store's Customer details and Sales details)

Hope you have already created your Data Cloud Org and also downloaded the Customer's details and Sales details from previous posts.  Now...