Friday 18 October 2019

Lightning Message Service (LMS) | MessageChannel

In Winter 20 Salesforce Introduce the new feature Lightning Message Service and it is generally available in Summer 20. Lightning Message Service (LMS) allow you to communicate between Visualforce and Lightning Components (Aura and LWC both) on any Lightning page. LMS API allow you to publish message throughout the lightning experience and subscribe the same message anywhere with in lightning page. It is similar to Aura Application Events to communication happens between components.


Lightning Message Service is based on a new metadata type: Lightning Message Channels. We need to use Lightning Message Channel to access the Lightning Message Service API.
  1. In LWC we can access Lightning Message Channel with the scoped module @salesforce/messageChannel
  2. In Visualforce, we can use global variable $MessageChannel
  3. In Aura, use lightning:messageChannel in your component

 

When to use Lightning Message Service. 

In Lightning Experience, if we want a Visualforce page to communicate with a Lightning web component then we have to implement a custom publish-subscribe solution because this is currently not possible with LWC Event. Now, we can use the Lightning Message Service API to handle this communication.
Lightning Message Service LMS and Message Channel


Let see how we can implements this

Create Lightning Message Channel :-

Currently we can create Lightning Message channel with Metadata API. You can create the same with the help of VsCode. I hope you have VsCode installed in your machine if not please check this post. You need to create one DX project then you need to place your message channel definition with the suffix .messageChannel-meta.xml in the force-app/main/default/messageChannels directory. like below folder structure.

MyMessageChannel.messageChannel-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
   <masterLabel>MyMessageChannel</masterLabel>
   <isExposed>true</isExposed>
   <description>This Lightning Message Channel sends information from VF to LWC</description>

   <lightningMessageFields>
       <fieldName>messageToSend</fieldName>
       <description>message To Send</description>
   </lightningMessageFields>

   <lightningMessageFields>
       <fieldName>sourceSystem</fieldName>
       <description>My source?</description>
   </lightningMessageFields>

</LightningMessageChannel>

And Package.xml should be like below
package.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
       <members>*</members>
       <name>LightningMessageChannel</name>
    </types>
    <version>47.0</version>
</Package>
Right click on LightningMessageChannel file and Deploy in Org.

 

Create Visualforce page and LWC

 

1) LMS with Visualforce page

Salesforce introduced new sforce.one APIs — publish, subscribe, and unsubscribe — in Visualforce to interact with LMS (only available in Lightning).

LMSVisualforcePage.page
<apex:page>
  
    <div>
        <p>Enter Your Message Here:</p>
        <input type="text" id="theMessage" />
        <button onclick="publishMC()"> Publish Msg</button>
        <br/><br/>
            <button onclick="subscribeMC()">Subscribe</button>
            <button onclick="unsubscribeMC()">Unsubscribe</button>
        <br/>
        <br/>
        <p>Received message:</p>
        <label id="MCMessageText"/>
    </div>
  
    <script>
      
        // Load the MessageChannel token in a variable
        var SAMPLEMC = "{!$MessageChannel.MyMessageChannel__c}";
        var subscriptionToMC;

        function publishMC() {
            const message = {
                messageToSend: document.getElementById('theMessage').value,
                sourceSystem: "From VisualForce Page"
            };
            sforce.one.publish(SAMPLEMC, message);
        }
      
        // Display message in the textarea field
        function displayMessage(message) {
            var textLabel = document.querySelector("#MCMessageText");
            textLabel.innerHTML = message ? JSON.stringify(message, null, '\t') : 'no message payload';
        }

        function subscribeMC() {
            if (!subscriptionToMC) {
                subscriptionToMC = sforce.one.subscribe(SAMPLEMC, displayMessage);
            }
        }

        function unsubscribeMC() {
            if (subscriptionToMC) {
                sforce.one.unsubscribe(subscriptionToMC);
                subscriptionToMC = null;
            }
        }

    </script>

</apex:page>

  • "subscribeMC" method is used to subscribe the Message Channel with "sforce.one.subscribe(SAMPLEMC, displayMessage);"
  • "unsubscribeMC" method to unsubscribe the Message Channel with "sforce.one.unsubscribe(subscriptionToMC);"
  • "publishMC" to publish the message withe the help of "sforce.one.publish(SAMPLEMC, message);"

2) LMS with Lightning Web Components

lMCWebComponentDemo.js
import { LightningElement,track } from 'lwc';
import { publish,subscribe,unsubscribe,createMessageContext,releaseMessageContext } from 'lightning/messageService';
import SAMPLEMC from "@salesforce/messageChannel/MyMessageChannel__c";

export default class LMCWebComponentDemo extends LightningElement {
    @track receivedMessage = '';
    @track myMessage = '';
    subscription = null;
    context = createMessageContext();

    constructor() {
        super();
    }

    handleChange(event) {
        this.myMessage = event.target.value;
    }

    publishMC() {
        const message = {
            messageToSend: this.myMessage,
            sourceSystem: "From LWC"
        };
        publish(this.context, SAMPLEMC, message);
    }

    subscribeMC() {
        if (this.subscription) {
            return;
        }
        this.subscription = subscribe(this.context, SAMPLEMC, (message) => {
            this.displayMessage(message);
        });
     }
 
     unsubscribeMC() {
         unsubscribe(this.subscription);
         this.subscription = null;
     }

     displayMessage(message) {
         this.receivedMessage = message ? JSON.stringify(message, null, '\t') : 'no message payload';
     }

     disconnectedCallback() {
         releaseMessageContext(this.context);
     }

}
  • First, we have to ensure that we import both the methods to interact with LMS
    import { publish,subscribe,unsubscribe,createMessageContext,releaseMessageContext } from 'lightning/messageService';
    import SAMPLEMC from "@salesforce/messageChannel/MyMessageChannel__c";

lMCWebComponentDemo.html
<template>
    <lightning-card title="LMC Web Component" icon-name="custom:custom14">
        <div class="slds-m-around_medium">
            <p>MessageChannel: MyMessageChannel__c</p>
            <br>
        </div>
        <!-- Default/basic -->
        <div class="slds-p-around_medium lgc-bg">
            <lightning-input type="text" label="Enter some text" value={myMessage} onchange={handleChange}></lightning-input>
            <lightning-button label="Publish" onclick={publishMC}></lightning-button>
        </div>

        <div class="slds-p-around_medium lgc-bg">
            <lightning-button label="Subscribe" onclick={subscribeMC}></lightning-button>
            <lightning-button label="Unsubscribe" onclick={unsubscribeMC}></lightning-button>
            <p>Latest Message Received</p>
            <lightning-formatted-text value={receivedMessage}></lightning-formatted-text>
        </div>

    </lightning-card>
</template>


Reference:

Some other related post:
  1. Event Communication in Lightning Web Components
  2. Event Communication in Lightning Components
  3. Setup VsCode for Salesforce
  4. Lightning We Components

Thanks
Amit Chaudhary


Thursday 17 October 2019

Lightning datatable In Lightning Web Components | lightning datatable inline edit

Welcome back, In this post we are going to create another lightning web component (LWC), Where we can search contact records and will display result using lightning-datatable lwc component. Lightning datatable tag is same as lightning:datatable tag in aura. In this lightning Datatable example we will also talk about lightning datatable inline edit.

lighning-datatable syntax:-
                    <lightning-datatable key-field="Id" 
                                            data={contacts} 
                                            columns={columns} 
                                            hide-checkbox-column="true" 
                                            show-row-number-column="true"
                                            > 
                    </lightning-datatable> 
  • If you want to hide the checkbox from table then add "hide-checkbox-column"
  • If you want to show row number then please add "show-row-number-column".
Let's see how we can create custom record search functionality in lightning web components. I hope VsCode is already setup on you machine and you know how to create Lightning Web Component. If not please check this post.

1) Create Apex Class

public with sharing class LWCDataTableExample {
    @AuraEnabled(Cacheable=true)
    public static List <Contact> getContacts(String strLastName) {
        String strLastNameLike = '%'+strLastName+'%';
        List<Contact> contList = [SELECT Id,FirstName,LastName,Account.Name
                                   FROM Contact
                                   Where LastName like :strLastNameLike
                                   LIMIT 10];
        return contList;
    }   
}

This apex class we will call from Lightning web components. If you want to learn more about how to call apex class from lightning web components then please check this post.

2) Create Lightning Web Components.

lwcLightningDataTableDemo.html
<template>
    <lightning-card title = "Search Contacts" icon-name = "custom:custom63"> 
        <div class = "slds-m-around_medium"> 
            <lightning-input type = "search" onchange = {handleKeyChange} class = "slds-m-bottom_small" label = "Search" >
            </lightning-input> 
            <template if:true = {contacts}> 
                <div style="height: 300px;"> 
                    <lightning-datatable key-field="Id" 
                                            data={contacts} 
                                            columns={columns} 
                                            hide-checkbox-column="true" 
                                            show-row-number-column="true"> 
                    </lightning-datatable> 
                </div>                  
            </template>
            <template if:true = {error}> 
                {error}> 
            </template> 
        </div> 
    </lightning-card> 
</template>


lwcLightningDataTableDemo.js
import { LightningElement,track } from 'lwc';
import getContacts from '@salesforce/apex/LWCDataTableExample.getContacts';

const columns = [ 
    { label: 'Id', fieldName: 'Id' }, 
    { label: 'First Name', fieldName: 'FirstName' }, 
    { label: 'Last Name', fieldName: 'LastName' }
];

export default class LwcLightningDataTableDemo extends LightningElement {
    @track contacts;
    @track error; 
    @track columns = columns;
   
    handleKeyChange( event ) { 
        const strLastName = event.target.value; 
        if ( strLastName ) { 
            getContacts( { strLastName } )   
            .then(result => { 
                this.contacts = result; 
                console.log('I am here',this.contacts);
               // console.log(JSON.stringify(result, null, '\t'));
   
            }) 
            .catch(error => { 
                this.error = error; 
            }); 
        } else 
        this.contacts = undefined; 
    }
}

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

</LightningComponentBundle>

NOTE:- Now from Winter 20 release we can Add Lightning Web Components as Custom Tabs for that we need to add lightning__Tab target to the component’s configuration file.

Now Your page will look like this


Lightning datatable inline edit


Let's see how we can do inline Edit
  • For inline editing we can use the uiRecordAPI. For that we need to import the "lightning/uiRecordApi"
  • We also need to add "onsave={handleSave}" and "draft-values={draftValues}"on lightning-datatable and on column we need to add enable "editable: true".
  • Then we need to add handler Save method.

Let's see how our code will look like:


lwcLightningDataTableDemo.html
<template>
    <lightning-card title = "Search Contacts" icon-name = "custom:custom63"> 
        <div class = "slds-m-around_medium"> 
            <lightning-input type = "search" onchange = {handleKeyChange} class = "slds-m-bottom_small" label = "Search" >
            </lightning-input> 
            <template if:true = {contacts}> 
                <div style="height: 300px;"> 
                    <lightning-datatable key-field="Id" 
                                            data={contacts} 
                                            columns={columns} 
                                            hide-checkbox-column="true" 
                                            show-row-number-column="true"
                                            onsave={handleSave}
                                            draft-values={draftValues}
                                            > 
                    </lightning-datatable> 
                </div>                  
            </template>
            <template if:true = {error}> 
                {error}> 
            </template> 
        </div> 
    </lightning-card> 
</template>


lwcLightningDataTableDemo.js
import { LightningElement,track } from 'lwc';
import getContacts from '@salesforce/apex/LWCDataTableExample.getContacts';

import { updateRecord } from 'lightning/uiRecordApi';
import { refreshApex } from '@salesforce/apex';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';


const columns = [ 
    { label: 'Id', fieldName: 'Id' }, 
    { label: 'First Name', fieldName: 'FirstName', editable: true  }, 
    { label: 'Last Name', fieldName: 'LastName', editable: true  }
];

export default class LwcLightningDataTableDemo extends LightningElement {
    @track contacts;
    @track error; 
    @track columns = columns;
    @track draftValues = [];
    handleKeyChange( event ) { 
        const strLastName = event.target.value; 
        if ( strLastName ) { 
            getContacts( { strLastName } )   
            .then(result => { 
                this.contacts = result; 
                // console.log('I am here',this.contacts);
                // console.log(JSON.stringify(result, null, '\t'));
            }) 
            .catch(error => { 
                this.error = error; 
            }); 
        } else 
        this.contacts = undefined; 
    }

    handleSave(event) {
        const recordInputs =  event.detail.draftValues.slice().map(draft => {
            const fields = Object.assign({}, draft);
            return { fields };
        });
   
        const promises = recordInputs.map(recordInput => updateRecord(recordInput));
       
        Promise.all(promises).then(contacts => {
            this.dispatchEvent(
                new ShowToastEvent({
                    title: 'Success',
                    message: 'All Contacts updated',
                    variant: 'success'
                })
            );
             // Clear all draft values
             this.draftValues = [];
   
             // Display fresh data in the datatable
             return refreshApex(this.contact);
        }).catch(error => {
            // Handle error
        });
    }


}

Please check below post on Lightning Web Components:-

Check our YouTube Channel for more recording in Lightning Web Components.

Reference :-
1) https://developer.salesforce.com/docs/component-library/bundle/lightning-datatable/example
2) https://developer.salesforce.com/docs/component-library/documentation/lwc/data_table_inline_edit


Please share your feedback.

Sunday 13 October 2019

Unlocked Packages | Modular Application Development Using Unlocked Packages | 2GP


Salesforce offers different types of packages (unmanaged, managed and Unlocked). Unlocked packages are especially suited for internal business apps and allowed you to do modular application development. Unlocked package is the right package type for most use cases, Unless you plan to distribute an app on AppExchange. You can use unlocked packages to organize your existing metadata, package an app, extend an app that you’ve purchased from AppExchange, or package new metadata.

Unlocked packages help you add, edit, and remove metadata in your org in a trackable way. You can install unlocked package to multiple orgs, and upgrade your Salesforce apps easier and faster. Metadata in unlocked packages can be modified in a production org ( You can do in case of emergency but not recommended).

Unlocked packages follow a source-driven development model. The source of truth of the metadata contained within the package is your version control system, not what’s in an org. This model brings with it all the advantages of modern source-driven development models.

We did one session on Unlocked Package in ApexHours With Mohith. Here is some highlight of that session.

What is Package ?

 

Packaging allows you to group various Salesforce Components (Apps, Tabs, Objects, Layouts, Workflows, Approval Process Flows, visualforce, apex, lightning components, profiles, permission sets and other metadata) for distribution or deployment. A package can also be defined as a container and allows for deployment of the components across various Salesforce organizations. Salesforce Set up UI and Salesforce CLI allows for creation of these. Package Creation yields a URL that allows it to install in various orgs


Difference Between Unmanaged package , Manage and Unlocked Package.

 

Unmanaged Package Managed Package Unlocked Package
Not upgradable  Upgradable and has namespace Upgradable and one can namespace them or choose not to
Metadata elements are not IP Protected Metadata elements are IP Protected Metadata elements are not locked and can be changed by system admins
Can we created in Salesforce UI Can be created in salesforce UI and distributed via appexchange Requires Salesforce CLI to generate them 
Unmanaged package containers automatically pull dependency Components are locked and one cannot modify them directly in production or sandbox Allows you to build your applications in a modular way

Allows for creation of extension packages Easier to manage when codebase is modularized

Modular Application Development Concepts

 

Break Your one monolithic app to multiple packages defining dependency between them. Unlock packages allow you to do them.


Building Unlocked packages using Salesforce CLI

 

Please check this recording for step by step process. Thank you so much Mohith for a great session in ApexHours. Here is agenda of session.
  1. Introduction To Salesforce Packaging
  2. Differentiate between unmanaged packages , managed packaged and unlocked packages
  3. Modular Application Development Concepts
  4. Building unlocked packages using salesforce cli
  5. Creating Dependency between packages
  6. Publishing and Installing Unlocked packages



Here is useful Salesforce DX command to create unlocked package. If you are new to SalesforceDX please check this recording.
  • One can create a package using below  
    sfdx force:package:create --name <name> --description <description> --packagetype Unlocked --path force-app --nonamespace --targetdevhubusername <Devhubalias>
  • Create a package version as below 
    sfdx force:package:version:create -p <packagename> -d force-app --wait 10 -v <Devhubalias>
  • Installation can also be done via the Salesforce CLI  
    sfdx force:package:install --wait 10 --publishwait 10 --package <packagename>@1.0.0-1 -r -u <alias>
  • By Default a BETA package is generated . We can promote a package using below 
    sfdx force:package:version:promote -p <packagename>@1.0.0-1 -v <DevHubalias>


References :-