Introduction to Web Components and Lightning Web Components

Here will I will provide a gentle introduction to Web Components. Readers new to Javascript can be benefited from this article. This can be a stepping stone to learning Salesforce Lightning Web Components (LWC).

Classes in Javascript

// Definitions
class Person {
constructor (name, city ) {
this.name = name;
this.city = city;
}}
class Customer extends Person {
constructor (name, city, amount) {
super(name, city);
this.amount = amount;
}
deposit(amount) {
this.amount += amount;
}
withDraw(amount) {
this.amount -= amount;
}
}

How to use Customer Object out this Customer class?

let myCust = new Customer('Johny Appleseed','New Found Land',  100);// deposit money 
myCust.deposit(200);
// withdraw some
myCust.withDraw(50);
console.log(myCust);
// assert the amount
console.assert(myCust.amount === 250, "Not adding up!");
// Update UIconst resultsEle = document.getElementById('results');
resultsEle.value = JSON.stringify(myCust, null, 4);

Async functions in Javascript — using fetch API

const timeUrl = 'https://mohansun-rum.herokuapp.com/time';async function  getTime() {
const options = { method: 'GET',
headers:
{ 'Content-Type': 'application/json' }
};
const response = await fetch(timeUrl, options);
const timeData = await response.json();
return timeData;
}

Use the async function getTime() using await

// get the time from REST service
const timeData = await getTime();
console.log(timeData);
// update UIconst restDataEle = document.getElementById('restData');
restDataEle.value = JSON.stringify(timeData, null, 4);

HTML (refer: https://webcomponents.dev/edit/DtwWthPN5POQO7wPpoCO)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Lesson - 1</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link rel="stylesheet" href='./css/l1-app.css'/>
</head>
<body class='container'><h3>Lesson - 1</h3><input type="button" title="Click to view the results" class='btn btn-primary' id='btnShowResults' value="Results" onclick="hanldeShowResults()"/>
<textarea class='form-control' name="results" id="results" cols="30" rows="10"></textarea>
<textarea readonly class='form-control' name="code" id="code" cols="30" rows="15"> </textarea>
<textarea readonly class='form-control' name="restData" id="restData" cols="30" rows="15"> </textarea><script src="js/l1-app.js"></script>

</body>
</html>

l1.app.js (refer: https://raw.githubusercontent.com/mohan-chinnappan-n/LWC-Classes/master/js/l1/js/l1-app.js)

// l1-app.js// Definitions
class Person {
constructor (name, city ) {
this.name = name;
this.city = city;
}
}
class Customer extends Person {
constructor (name, city, amount) {
super(name, city);
this.amount = amount;
}
deposit(amount) {
this.amount += amount;
}
withDraw(amount) {
this.amount -= amount;
}
}
const timeUrl = 'https://mohansun-rum.herokuapp.com/time';
async function getTime() {
const options = { method: 'GET',
headers: { 'Content-Type': 'application/json; charset=utf-8' }
};
const response = await fetch(timeUrl, options);
const timeData = await response.json();
return timeData;
}// Handler
async function hanldeShowResults(event) {
// create a new customer
let myCust = new Customer('Johny Appleseed','New Found Land', 100);
// deposit money
myCust.deposit(200);
// withdraw some
myCust.withDraw(50);
console.log(myCust);
console.assert(myCust.amount === 250, "Not adding up!");
const resultsEle = document.getElementById('results');
resultsEle.value = JSON.stringify(myCust, null, 4);
const timeData = await getTime();
console.log(timeData);
const restDataEle = document.getElementById('restData');
restDataEle.value = JSON.stringify(timeData, null, 4);

}
// render function code
const codeEle = document.getElementById('code');
codeEle.value = hanldeShowResults.toString();

Style Sheet (refer: https://raw.githubusercontent.com/mohan-chinnappan-n/LWC-Classes/master/js/l1/css/l1-app.css)

/* l1-app.css */

textarea {
font-family: Monaco, Arial, Helvetica, sans-serif;
}

#code {
background-color: black;
color:whitesmoke;
}

http://localhost:7010/lwc-class/js/l1/l1.html

Let us write a simple Web Component

Web Component Definition and usage (refer: https://github.com/mohan-chinnappan-n/LWC-Classes/blob/master/wc/hw.html)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hello World Web Component</title>
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous"
/>
</head>
<body class="container">
<h3>Simple Web Component</h3>
<style>
img {
border-radius: 50%;
border: 1px solid #99ccff;
}
</style>
<img
width="200"
src="https://avatars.githubusercontent.com/u/5963194?s=460&v=4"
/>
<script>
class HelloFarmingWorld extends HTMLElement {
constructor() {
// Always call super first in constructor
super();
// -- write element functionality in here
// Attach a shadow root to the element.
let shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.innerHTML = `<p>Hello Farming World!</p>
<p>Remembering Johnny Appleseed and G. Nammalvar!</p>
<img width="400" src='https://img.apmcdn.org/5bb55eea8ffc20aef57401b38fdcea93acca15a8/widescreen/8864a0-20200529-johnny-appleseed-04.jpg'/>
<img with="400" src='https://im.rediff.com/money/2010/mar/17nam4.jpg'/>
<hr/>
`;
const button = document.createElement("button");
button.setAttribute('class', 'btn-primary');
button.onclick = () => alert("Yes!");
button.innerText = "Farming Rules!";
shadowRoot.append(button);
// Create some CSS to apply to the shadow dom
const style = document.createElement('style');
style.textContent = `
img { border-radius: 50%;
border: 3px solid #99ccff;
}.btn-primary {
font-weight: 400;
text-align: center;
line-height: 1.5;
font-size: 1rem;

color: #fff;
background-color: #007bff;
border-color: #007bff;
border:1px solid #99ccff;
padding: 4px;
`;
// Attach the created elements to the shadow dom
shadowRoot.appendChild(style);
}
}
customElements.define("hello-farming-world", HelloFarmingWorld);
</script>
<hello-farming-world></hello-farming-world>
</body>
</html>

Shadow DOM

Using Templates and Slots

Example using template and slots (https://github.com/mohan-chinnappan-n/LWC-Classes/blob/master/js/l2/l2.html)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lesson 2 </title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body class="container">
<h3>Quotes for Today!</h3>
<style>
img { border-radius: 50%;
border: 2px solid #ffcc99;
}
</style>
<template id="quote">
<style>
img { border-radius: 50%;
border: 2px solid #99ccff;
}
</style>
<p>

<span> <slot name="quote"> QUOTE MISSING</slot>
- <slot name="author"><span>AUTHOR MISSING</span></slot>
<slot name="image">
<img src='https://mohan-chinnappan-n.github.io/gandhi-mahatma-large.jpg' width="40"/>
</slot>
</span>

<hr/>
</p>
</template>
<script>customElements.define('my-quote',
class extends HTMLElement {
constructor() {
super();
let template = document.getElementById('quote');
let templateContent = template.content;

const shadowRoot = this.attachShadow({mode: 'open'});
// Create some CSS to apply to the shadow dom
const style = document.createElement('style');
style.textContent = `
img { border-radius: 50%;
border: 2px solid #99ccff;
}
`;
// Attach the created elements to the shadow dom
// shadowRoot.appendChild(style);
// note the above style comes from the template itself
shadowRoot.appendChild(templateContent.cloneNode(true));}
}
);
</script>
<my-quote>
<span slot="quote">An ounce of practice is worth more than tons of preaching.</span>
<span slot="author">Mohandas K Gandhi</span>
<span slot="image"> <img src='https://mohan-chinnappan-n.github.io/gandhi-mahatma-large.jpg' width="40"/>
</span>
</my-quote>
<my-quote>
<span slot="quote">Earth provides enough to satisfy every man's needs, but not every man's greed. </span>
<span slot="author">Mohandas K Gandhi</span>
<span slot="image"> <img src='https://mohan-chinnappan-n.github.io/gandhi-mahatma-large.jpg' width="40"/>
</span>
</my-quote>
<my-quote>
<span slot="quote">You have to dream before your dreams can come true.</span>
<span slot="author">A.P.J Abdul Kalam</span>
<span slot="image"> <img src='https://mohan-chinnappan-n.github.io/about/img/apj.png' width="40"/>
</span>
</my-quote>
<my-quote>
<span slot="quote">Our virtues and our failings are inseparable, like force and matter. When they separate, man is no more.</span>
<span slot="author">Nikola Tesla</span>
<span slot="image"> <img src='https://mohan-chinnappan-n.github.io/about/img/teslaN.jpg' width="40"/>
</span>
</my-quote>
<my-quote>
</my-quote>
</body>
</html>

Let us build a Web Component to show counters


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lesson 4 </title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body class="container">
<h3>Let us build Counters</h3>
<style>
img { border-radius: 50%;
border: 2px solid #ffcc99;
}
</style>
<template id="counter"><style>
button { border-radius: 50%;
border: 4px solid #99ccff;
background-color: steelblue;
color: white;
width: 4rem;
height: 4rem;
}
</style>
<button id="dec">-</button>
<span id="count"></span>
<button id="inc">+</button>
</template>
<script>customElements.define('my-counter',
class extends HTMLElement {
constructor() {
super();
let template = document.getElementById('counter');
let templateContent = template.content;

const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(templateContent.cloneNode(true));
this.count = 0;
}connectedCallback() {
this.shadowRoot.getElementById("inc").onclick = () => this.inc();
this.shadowRoot.getElementById("dec").onclick = () => this.dec();
this.update(this.count);
}
inc() {
this.update(++this.count);
}

dec() {
this.update(--this.count);
}

update(count) {
this.shadowRoot.getElementById("count").innerHTML = count;
}
}
);
</script>
<my-counter></my-counter>
<hr/>
<my-counter></my-counter>
</body>
</html>

Refer:

Let us write the Quotes Web Component in Lightning Web Component

Refer: https://webcomponents.dev/edit/9jZzL2g3QRRlLMUBSLZG

LWC — Getting data from REST Service

Refer: https://webcomponents.dev/edit/jbMtcVLV11DOGzY9A5MA

Demo of the Application

Sorting on the datatable demo

Row Actions on datatable row

<template><h3 class='slds-text-heading_large'>Cards of Quotes</h3><template for:each={quotes} for:item="quote"><c-child key={quote.author} title={quote.author} body={quote.text} image={quote.image}></c-child></template><h3 class='slds-text-heading_large'>Carousel of Quotes</h3><lightning-carousel><template for:each={quotes} for:item="quote"><lightning-carousel-image key={quote.author} src={quote.image} header={quote.author} description={quote.text}alternative-text="First card accessible description." href="javascript:void(0);"></lightning-carousel-image></template></lightning-carousel></template>

Dual-List of Fruits

Refer: https://webcomponents.dev/edit/ewtUvi0wyFuW2kr52Jv2

Improving Agriculture, Healthcare… Equality for All, Love and Respect Nature. https://mohan-chinnappan-n.github.io/about/cv.html

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store