Thursday, 26 December 2019

Lightning web Components


why using Lightning web components ?
1.Re-usability     - Imports,Exports and Templates allows us to reuse the components.
2.Composition      - Breaking components to smaller chunks
3.Encapsulation    - Using the shadow DOM
4.Standardization  - Part of W3C specs and supported by majority of browser.

Core Elements of web Components ?

1.HTML Templates
2.Custom Elements
3.Shadow DOM
4.ES Modules

Templates and Slots :
=====================
1.Templates allow us to load HTML tags without rendering them on initial load.
2.Write once and reuse in multiple pages or multiple apps.
3.Attach them to Shadow DOM or DOM.
4.Slots are placeholders in template that allow you to insert your own HTML markup.

Custom Elements :
=================
Allows us to define an element and let browser learn about the new tag.

class MyCustomElement extends HTMLElement{
 
  constructor(){
    // always call super() first in your constructor to inherit from your Parent class
super();
  }

}
customElements.define('my-custom-element',MyCustomElement);

Shadow DOM :
==============
1.Shadow DOM allows the web browser to render DOM elements without putting them into the main document DOM tree.
2.One can attach shadow DOM to an element using the API Element.attachShadow(mode:open}).
3.You can attach the Shadow to the custom element.
4.Offers encapsulation (CSS in outer DOM cannot bleed into the shadow DOM).

Lightning Web Components (LWC)

1.Lightning Components used aura framework and its renamed as Aura Components.
2.Components built using Web Components standard now are referred as lightning web components.
3.Lightning web components(LWC) adheres closely to the web components standards.
4.Modules for Data services, UI API and provides reactive properties.


Note : Shadow DOM
1.The elements in each Lightning web component are encapsulated in a shadow tree.
2.This part of the DOM is called a shadow tree because it's hidden from the document that contains it.
3.The shadow tree affects how you work with CSS,events and the DOM.
4.Since not all browsers that Salesforce supports implement Shadow DOM,LWC uses a shadow DOM polyfill.

ex :
<c-todo-app>
 #shadow-root
   <div>
    <p>Your To Do List</p>
   </div>
   <c-todo-item>
    #shadow-root
<div>
   <p>Go to the store</p>
</div>
</c-todo-item>
</c-todo-app>

Shadow tree for todoApp
Shadow tree for todoItem

Javascript Properties :
======================
1.Reactive Property - Change in property value automatically reRenders the template.
2.To create a public property use @api.
3.To create a private property and reRender DOM use @track.

Lightning Data Service :
==========================
1.Mark the property with @wire if you want the property to receive the changes when data change happens to record.
2.LWC supports UI APIs and Metadata describes.

Life Cycle Hooks :
===================
1.constructor()             - When Component Created
2.connectedCallback()       - When Component inserted in DOM.
3.disconnectedCallback()    - When Component removed from DOM.
4.render                    - Called after ConnectedCallBack.For Complex rendering Logic.
5.renderedCallback()        - After render
6.errorCallback(error,stack)- If any error in component life-cycle methods.

Important point :
==================

Handle Event When Component not in Same DOM

1.Use pub sub model

Component :
============
1.Component HTML File
2.Component Javascript File.
3.Component Configuration File.

Javascript files in Lightning web components are ES6 modules.
By default, everything declared in module is local-it's scoped to the module.

To import a class,function or variable declared in module,use the export statement.

ex :

import { LightningElement } from 'lwc';
export default class MyComponent extends LightningElement{

}

lwc is core module from wher we import LightningElement.
LightningElement is custom wrapper of the standard HTML element creates javascript class for LWC.

Decorators :
=============
The Lightning Web Components programming model has three decorators
that add functionality to a property or function.
The ability to create decorators is part of ECMAScript,but these three decorators
are unique to Lightning Web Components.

1.@api :

To expose a public property,decorate it with @api.
An owner component that uses the component in its markup can
access the component's public properties.

2.@track :

To track a private property's value and re render a component
when it changes,decorate the property with @track.
Tracked properties are also called private reactive properties.

3.@wire :

To read Salesforce data, Lightning web components use a reactive wire
service . When the wire service Provisions data, the component rerenders.

Components use @wire in their javaScript class to specify a wire adaptor or an Apex method.

Components use @wire in their javaScript class to read data from one of the wire adapter
in lightning/ui*Api namespace.

wire adapters :
==============
1.getListUi
2.getObjectInfo
3.getPicklistValues
4.getPicklistValuesByRecordType
5.getRecord
6.getRecordUi

Note :
The wire service delegates control flow to the Lightning web Component engine.
Delegating control is great for read operations,bit it isn't great for create and
delete operations.As a developer ,you want complete control over operations
that change data.use Javascript API instead of wire service.


Call Apex Method using wire service :
=====================================

ex:
import { LightningElement,track,Wire } from 'lwc';
import findContacts from '@salesforce/apex/ContactController.findContacts';

export default class ApexWireMethodToProperty extends LightningElement{
  @track searchkey='';
  @wire (findContacts,{ searchkey : 'searchkey'}) contacts;
 
}

Imperative Methods :
=====================
import { LightningElement,track,Wire } from 'lwc';
import getContactList from '@salesforce/apex/ContactController.getContactList';

export default class ApexImperativeMethod extends LightningElement{
 
   @track contacts;
   @track error;
 
   handleLoad(){
       getContactList()
        .then(result => {
           this.contacts=result;
         })
         .catch(error => {
          this.error=error;
         });  
 
   }

}

Javascript logic :
===================
1. ES6 classes are used to define logic.
2. ES6 module imports/exports are used to import and export logic.

ex :

 @api recordId;
 @wire(getRecord,{recordId : $recordId',fields});

 In the wire adapter's configuration object,prefix a value with $ to reference a property of the component instance.
 The $ prefix tells the wire service to treat it as a property of the class and evaluate it as this.PropertyName.
 The property is reactive. If the property's value changes,new data is provisioned
 and the component rerenders.

 Lightning web component Communication :
 ========================================

 1.Public Properties (Passing data down)

 ex :
   child component
 
   import { LightningElement,api} from 'lwc';
 
   export default class ChildComponent extends LightningElement{
     @api myProperty ='DeafultValue';
   }
 
  Parent Component
 
  <template>
     <c-child-component my-property={parentCmpProperty}></c-child-component>
  </template>
 
  Note : In child component you can assign a default value to a public property,
  but you should never change the value of a public property in the component itself.
  Remember the purpose of this property is to the Public API of the component,
  so only the parent component should set or change its value.
 
 2.Public methods (passing data down)

 public methods can receive parameters and return values.

  ex :
 
  child Coponent
 
  import { LightningElement, api } from 'lwc';

export default class ChildComponent extends LightningElement {
    @api
    doWhatever(param1) {
        // Do whatever...
        return 'Finished!';
    }
}

 Parent component

 import { LightningElement } from 'lwc';

export default class ParentComponent extends LightningElement {
   
   
        const returnValue = this.template.querySelector('c-child-component').doWhatever('My param');     
   
}

3.Custom Event (Passing data up)

ex :

 child component

 import { LightningElement } from 'lwc';

export default class ChildComponent extends LightningElement {
 
        const myEvent = new CustomEvent('eventname',
                                        {
                                            detail: {
                                                prop1: value
                                            }
                                        });
        this.dispatchEvent(myEvent);
   
}

parent component (event listener declaratively)

<template>
    <c-child-component oneventname={handleEvent}></c-child-component>
</template>

import { LightningElement } from 'lwc';

export default class ParentComponent extends LightningElement {
    handleEvent(event) {
        // Do whatever with event.detail
    }
}

Note : Event type Naming conventions ( Here "eventname" is Event Type)
1.No uppercase letters.
2.No spaces
3.Use underscores to separate words.
4.Don't prefix your event name with string 'on'.


ex : event Listener Pro-grammatically

child component

import { LightningElement } from 'lwc';

export default class ChildComponent extends LightningElement {
    handleResponse(event) {
        event.target.disabled = true;
        const btnName = event.target.label;
        const answerEvent = new CustomEvent("answer", { detail: btnName, bubbles: true });
        this.dispatchEvent(answerEvent);
    }
}

Parent Component

import { LightningElement, track } from 'lwc';

export default class ParentComponent extends LightningElement {
    @track answer = "Child: ";
    question = "Parent: Have you completed study for today?";

    constructor() {
      super();   
      this.template.addEventListener('answer', this.handleAnswer.bind(this));
    }
 
    handleAnswer(event) {
      /*eslint-disable-next-line*/
      console.log("res--&gt;" + event.detail);
      const res = event.detail;
      this.answer = this.answer + res;
    }
}

Note :

1.Add an event Listener to an element with in a shadow boundary, use this.template.addEventListener() method.
2.Add an event Listener to an element that the template doesn't own, use this.addEventListener() method directly.

 -> "this" refers to the default class.
 -> Container component can add event listeners and access elements directly on "this".
    so,we should use this.addEventListener and this.querySelector.
 -> Owner component has to add event-listeners and identification through "template".
    so,we should use this.template.addEventListener and this.template.querySelector.

ex : consider 3 components Grandparent, Parent and child

GrandParent

<template>
    <div>Grandparent:</div>
    <c-parent>
        <span slot='myslot'>
            <c-child></c-child>
        </span>
    </c-parent>
</template>

export default class Grandparent extends LightningElement {
    constructor() {
        super();
        console.log('this => ', this);       
        this.template.addEventListener('myevent', this.myeventHandlerTemplate);
    }
    renderedCallback() {       
        console.log("Grandparent renderedCallback template => ", this.template.querySelector('c-child'));
    } 
    myeventHandlerTemplate(event) {
        console.log('Grand parent template - myevent handled');
    }
}

Note :
GrandParent component is the owner of c-child component.
c-parent component is the container of c-child component.

Parent component

<template>
    <div>
        <slot name='myslot'></slot>
    </div>
</template>

export default class Parent extends LightningElement {
    constructor() {
        super();
        console.log('this => ', this);
        this.addEventListener('myevent', this.myeventHandler);
       
    }
    renderedCallback() {
        console.log("parent renderedCallback => ", this.querySelector('c-child'));
     
    }
    myeventHandler(event) {
        console.log('parent - myevent handled');
    }   
}

child component

export default class Child extends LightningElement {
    connectedCallback() {
        this.dispatchEvent(new CustomEvent('myevent', { bubbles: true }));
    }
}

Note :
bubbles:true and composed:false (default is false) will make the event bubble up until shadow boundary.

Out put :

this =>  Grandparent {setAttribute: ƒ}
this =>  Parent {setAttribute: ƒ}
parent - myevent handled
Grand parent template - myevent handled
parent renderedCallback =>  c-child
Grandparent renderedCallback template =>  c-child



4.pubsub event

1.pubsub is a singleton library that follows publish subscribe pattern.
2.Each component that subscribes to the event receives the event.

ex :

fire the event

 <template>
    <lightning-card title="Source" >
        <div class="slds-m-around_medium">
            <lightning-input type="text" onchange={handleChange} class="slds-m-bottom_small" label="Enter Text"></lightning-input>
        </div>
    </lightning-card>
</template>

import { LightningElement,wire} from 'lwc';
import { CurrentPageReference } from 'lightning/navigation';
import { fireEvent } from 'c/pubsub';
export default class Publishsource extends LightningElement {
    @wire(CurrentPageReference) pageRef;
    handleChange(event) {
        fireEvent(this.pageRef, 'inputChangeEvent', event.target.value);
    }
}

subscribe event

<template>
    <lightning-card title="Target">

        {inpVal}
    </lightning-card>
</template>

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

export default class Publishreceiver extends LightningElement {
      @track inpVal;
      @wire(CurrentPageReference) pageRef;

      connectedCallback() {
          // subscribe to inputChangeEvent event
          registerListener('inputChangeEvent', this.handleChange, this);
      }

      disconnectedCallback() {
          // unsubscribe from inputChangeEvent event
          unregisterAllListeners(this);
      }

      handleChange(inpVal) {
          this.inpVal = inpVal;
      }
}


Lightning Data service :
=========================
1.Caches results on the client.
2.Invalidates cache entry when data and meta data changes.
3.Optimizes server calls.

UI API :
============
1.Public Salesforce API to build UI.
2.Gives data and metadata in a single response.
3.UI responses respect CRUD,FLS and Sharing settings.

Base Lightning Components :
==========================
1.Lightning Record Form

supports edit,view and read-only modes.

2.Lightning Record View Form

displays a read-only form.

3.Lightning Record Edit Form

displays an editable form.

These Base Lightning Components works on top of the Lightning Data service.

Advantages of Base Lightning Components :
1.Metadata aware (data types etc).
2.Does not require Apex code
3.Provide Form Layout
4.Field labels are displayed based on Org's defaults.
5.Built on Lightning Data service and UI API.
6.Can access model statically.
7.Can also access model dynamically.


Overriding Standard Actions :
=============================
we can not directly override action with Lightning web component directly.
You need to call your component in Aura Component and use that aura component to override the action.

Lwc in Lightning Out :
======================
its possible to use LWC in visualforce as lightning out.


Composition :
==============

Owner

1.Set public properties on child.
2.Call public methods on child.
3.Listen for any events from child.

Container

1.Can read public properties on child.
2.Call public methods on child.
3.Listen for events bubbled from child.


Event Propagation Phases
========================
Event propagation can be controlled using two properties on the event.
1.bubbles
2.composed

bubbles :

1.A Boolean value indicating whether the event bubbles up through the DOM or not.
2.Defaults to false.

composed :

1.A Boolean value indicating whether the event can pass through the shadow boundary.
2.Defaults to false.

controlling Event Propagation :
===============================

1.bubbles:false and composed:false
The event doesn't bubble up through the DOM and doesn't cross the shadow boundary.

2.bubbles:true and composed:false
The event bubbles up through the DOM,but doesn't cross the shadow boundary.

3.bubbles:true and composed:true

The event bubbles up through the DOM,crosses the shadow boundary,and continues bubbling up through the DOM to the document root.

4.bubbles:false and composed:true

Lightning web components doesn't use this configuration.


Working with Third-Party Libraries :
=====================================

In LWC static resource is imported through "import" command
and then loadScript or loadStyle methods can be used to load the third-party library.

ex :

import { LightningElement } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import D3 from '@salesforce/resourceUrl/d3';
import DATA from './data';

export default class LibsD3 extends LightningElement {
    svgWidth = 400;
    svgHeight = 400;

    d3Initialized = false;

    renderedCallback() {
        if (this.d3Initialized) {
            return;
        }
        this.d3Initialized = true;

        Promise.all([
            loadScript(this, D3 + '/d3.v5.min.js'),
            loadStyle(this, D3 + '/style.css'),
        ])
            .then(() => {
                this.initializeD3();
            })
            .catch(error => {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error loading D3',
                        message: error.message,
                        variant: 'error',
                    }),
                );
            });
    }

Lightning Locker :
==================

Lightning Locker is a layer which sits in between your browser and DOM (document object).
In other words, Lightning Locker is a virtual browser that allows only secure request to go through.
This virtual browser sits in front of your real browser which is unsafe as you get access to complete DOM and can easily manipulate it.

No comments:

Post a Comment