Wednesday, July 31, 2019

PubSub event in Lightning Web Component 


Description : Event is used when we have to send some data from one component/LWC to the other. 
When the relationship between two components is Parent-Child, we can send data directly using regular events, discussed in the post. But when we have the Sibling-relationships, i.e. the two components are not linked with each-other but one of them needs inputs from the other for some operations, we need the Pub-Sub (Publisher-Subscriber) event-model.

For pub-sub model to be executed, make sure both the components are on the same DOM.


--------------- pubSub.js (a stand-alone LWC) ---------------

/**
 * A basic pub-sub mechanism for sibling component communication
 *
 * TODO - adopt standard flexipage sibling communication mechanism 
        when it's available.
 *
 * Temporarily removing all the pageRef checking in pubsub - not 
    supported in communities
 * https://salesforce.stackexchange.com/questions/252021/
    lightning-web-component-use-publish-subscribe-event-in-community-cloud
 * https://gist.github.com/kmesic/262887799fb70be94707cb0b87936e7b
 */

const events = {};

/**
 * 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 = (eventNamecallbackthisArg=> {

    if (!events[eventName]) {
        events[eventName] = [];
    }

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

    if (!duplicate) {
        events[eventName].push({ callbackthisArg });
    }
};

/**
 * 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 = (eventNamecallbackthisArg=> {
    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 = (eventNamepayload=> {
    if (events[eventName]) {
        const listeners = events[eventName];
        listeners.forEach(listener => {
            try {
                listener.callback.call(listener.thisArgpayload);
            } catch (error) {
                // fail silently
            }
        });
    }
};
const utility = {
    navigateToPage: (pageNameurlParams = ""=> {
        window.open(`/er/s/${pageName}?${urlParams}`'_self');
    }
};

var sharedDataForLeads = {};
export {
    registerListener,
    unregisterListener,
    unregisterAllListeners,
    fireEvent,
    utility,
    sharedDataForLeads
};

--------------- Sibling 1 (firing the Event) ---------------

.html -->>

<!--firing the event at some onclick/onchange event : -->
<template>
    <lightning-card>
        <lightning-input value={datVal} onchange={setInputVal} 
            label="The msg\">
        </lightning-input>
    </lightning-card>
</template>
.js -->>

import { LightningElement } from 'lwc';
import { fireEvent } from 'c/pubSub';

export default class PubsubFireCmp extends LightningElement {

    datVal = '';

    setInputVal(event){
        this.datVal = event.detail.value;

        fireEvent('msg'this.datVal);
        //msg -> an arbitary event name
        console.log(" ->> event fired ");
    }
}


--------------- Sibling 2 (subscribing the Event) ---------------

.html -->>
<template>
    <lightning-card>
        The message is : {myMsg}
    </lightning-card>
</template>
.js -->>

import { LightningElementwire } from 'lwc';
import { CurrentPageReferenceNavigationMixin } from 
        'lightning/navigation';
import {  registerListener } from 'c/pubSub';

export default class PubsubHandleCmp extends 
                NavigationMixin(LightningElement) {

    @wire(CurrentPageReferencepageRef;

    connectedCallback() //the subscriber method to be added 
                        inside connectedCallback method
    {
        registerListener('msg',this.getVal,this);
        //'msg' is the same event name being fired
        //getval is the method using the event data for business operations
    }

    myMsg = '';
    getVal(val    //val is the data sent during the event
                    component
    {
        this.myMsgval;
        console.log('message '+this.myMsg);
    }
}

Monday, July 29, 2019

Call Custom Labels in Apex:

String getLabel = Label.Label_API_Name;

Call Custom Labels in Lightning Component:

{!$Label.c.Label_API_Name}

Call Custom Labels in Lightning Javascript Controller:

var getLabel = $A.get(“{!$Label.c.Label_API_Name}”);

Tuesday, July 23, 2019

Parent-Child Communication in LWC


Sometimes we need data to be flown from Child-LWC to the parent (achieved through event), while sometimes we require data to be flown from parent to the child.


Here we deal with the flow from Parent (parentLWC) to Child (childLWC) :


***parentLWC***

html


<div title="Child LWC">

          <c-child-L-W-C clientid={records1}></c-vij-L-W-C>

</div>



***childLWC***

html


<template>

        <lightning-card title="Child Clientssss" icon-name="custom:custom14">

                <template if:true={bool}>

                <template for:each={newData} for:item="row" for:index="index" >

                       <------- show data ------->

                </template>

                </template>

         </lightning-card>

</template>


---
JS
---


import { LightningElement,wire, track, api } from 'lwc';


export default class childLWC extends LightningElement {

 @track newData=[];

 @track bool=false;

 @api

 get clientid() {

  // eslint-disable-next-line no-console

  return this._clientid;

 }


  set clientid(value) {

  if(value){

   this._clientid = value;

   for(var i=0; i<value.length; i++){

    this.bool=true;

    this.newData.push(value[i]);

   }

  }

  else{

   this.bool=false;

   this._clientid = undefined;

   this.newData = [];

  }

 }

}

Remove first and last characters of a String in Apex

Use Substring method:
    sampleString.substring(1, sampleString.length() - 1);

Monday, July 22, 2019

Embed a Lightning Web Compoent (LWC) inside another LWC

Parent-child Communication in LWC

We can call LWC inside another LWC just as aura:component inside another aura:component.

To make this, we just need to put some extra considerations of letter-cases.

For example :  
If we have an LWC "test" to be embedded inside a parent Lightning Web Component
      <c-test></c-test>

If we have LWC "lWC_Test" to be embedded inside the parent LWC 
      <c-l-W-C_-Test></c-l-W-C_-Test>

Sunday, July 21, 2019

System.QueryException: Only variable references are allowed in dynamic SOQL/SOSL


You need to check if you have put a colon ':' in between the query string.

Code-snippet for a dynamic SOQL - 

String squery = 'SELECT Id, Name, recordTypeId FROM Account';
squery += ' where recordTypeId =\''+ devRecordTypeId+'\'';

Database.query(squery);
System.runAs()

Usages in test classes - 
System.runAs(new User(Id = UserInfo.getUserID()){}
MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): User, original object: Account

This error is so common when attempting to create user and other objects records in a single transaction in apex. 

Workaround in apex class/trigger : use future method for creating user when encountered the error

Workaround in test class : don't try creating a new user data, instead use <System.runAs(new User(profileId =<required_Profile>))>

Since the test class needs to be Annotated SeeAllData=false, developer should try creating a TestUtil class to fetch the user data and call the same in the test class, code-snippet as below : 

TestUtil.cls : 
public static fetchRequiredProfileUser(){
 return [SELECT ID, Name FROM User WHERE ProfileId ='**********'];
}

Test Class :
@isTest private void useTest(){
 System.runAs(new User(TestUtil.fetchRequiredProfileUser())[0]);
}