Day: February 7, 2019

Lightning Web Component Reflect JavaScript Properties as a HTML Attributes

Let’s understand here how we can able to set the HTML properties from the lightning web component properties which are almost similar like how you are setting HTML attributes using the createAttribute() method. You can control whether public JavaScript properties appear as attributes in the rendered HTML of a Lightning web component. Allowing properties to appear as attributes are especially important when creating accessible components because screen readers and other assistive technologies use HTML attributes. All HTML attributes are reactive by default. When an attribute’s value changes in the component HTML, the component is re-rendered.When you take control of an attribute by exposing it as public property, the attribute no longer appears in the HTML output by default. To pass the value through to the rendered HTML as an attribute (to reflect the property), define a getter and setter for the property and call the setAttribute() method. You can also perform operations in the setter. Use a private property to hold the computed value. Decorate the private property with @track to make the property reactive. If the property’s value changes, the component rerenders.

 

In this example, we will be seeing how to set the title to a component on mouseover.

Create a lightning web component using the below SFDX command.

 sfdx force:lightning:component:create --type lwc --componentname mycomponent --outputdir force-app\main\default\lwc

 

Use the below mycomponent.html code

<template>
    <h1> Component </h1>
</template>

 

Use the below mycomponent.js code. This javascript controller title as public property. It converts the title to uppercase and uses the tracked property privateTitle to hold the computed value of the title. The setter calls setAttribute() to reflect the property’s value to the HTML attribute

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

export default class Mycomponent extends LightningElement {
    @track privateTitle;
    @api
    get title() {
        return this.privateTitle;
    }
    set title(value) {
        this.privateTitle = value.toUpperCase();
      this.setAttribute('title', this.privateTitle);
    }
}

 

Use the below mycomponent.js-meta.xml code

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

 

 

Create a new lightning web component using this SFDX command. we will be calling the my-component from this parent component.

 

 sfdx force:lightning:component:create --type lwc --componentname parent1 --outputdir force-app\main\default\lwc

 

Use this parent1.html code

<template>
<c-mycomponent title="Hover Over the Component to See Me"></c-mycomponent>
</template>

 

Push the changes to scratch org and add the changes to page layout and you can able to see the title on mouseover as shown below.

 

 

 

To make sure that you understand how JavaScript properties reflect to HTML attributes, look at the same code without the call to setAttribute(). The generated HTML doesn’t include the title attribute. Now update the mycomponent.js as shown below.

 

update the mycomponent.js code  as shown below

 

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

export default class Mycomponent extends LightningElement {
    @track privateTitle;
    @api
    get title() {
        return this.privateTitle;
    }
    set title(value) {
        this.privateTitle = value.toUpperCase();
     // this.setAttribute('title', this.privateTitle);
    }
}

Push the changes to scratch org. Now when you mouseover on the parent component you dnt see the title appears. Similarly, you can use removeAttribute() to hide HTML attributes from the rendered HTML.

 

Lightning Web Component Reactive Properties

Let’s understand here what is Reactive Properties. If the value of reactive property changes, the component’s template rerenders. When you modify reactive property the template view updates. A reactive property can be public or private.

Public Reactive Properties

Public properties define the public API for a component. A parent component that uses the component in its markup can access the
component’s public properties. Public properties are reactive. If the value of reactive property changes, the component’s template
rerenders any content that references the property. To mark a property as public, annotate it with the @api decorator. When you use the @api decorator, you must import it explicitly from the engine. For example:

import { api } from 'LWC';

Use decorators to add functionality to a property. A property can have only one decorator. For example, a property can’t
have @api and @track (private reactive property) decorators.

Create a lightning web component using the below SFDX command. In  TodoItem class with an itemName public property.

sfdx force:lightning:component:create --type lwc --componentname todoitem --outputdir force-app\main\default\lwc

Use the below todoitem.html

<template>
    <div class="view">
        <label>{itemName}</label>
    </div>
</template>

Use the below todoitem.js . In this JavaScript class, we have an itemName public reactive property

import { LightningElement,api } from 'lwc';
export default class Todoitem extends LightningElement {
    @api itemName;
}

Use the below todoitem.js-meta.xml

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

 

Create a Container component using the below SFDX command.  In this component, we will be passing the itemname.

 sfdx force:lightning:component:create --type lwc --componentname todoapp --outputdir force-app\main\default\lwc

Use below todoapp.html code. When you use the c-todoitem component in another component, you can set the itemName property. Property names in JavaScript are in camel-case while HTML attribute names are in kebab-case (dash-separated) to match HTML standards. In todoapp.html, the item-name attribute in markup maps to the itemName JavaScript property of c-todoitem.

<template>
    <div class="listing">
        <c-todoitem item-name="Milk"></c-todoitem>
        <c-todoitem item-name="Bread"></c-todoitem>
    </div>
</template>

 

A component that declares a public reactive property can’t set the property value, except at component construction time.
A parent component that uses the component in its markup can set the component’s public property value. In our example, the
c-todoitem component can’t update the value of the itemName property in the todoitem.js file.

 

Push the changes to scratch org and you can able to see the output as shown below.

 

 

To execute logic each time a public property is set, write a custom setter. If you write a setter for a public property, you must also write
a getter. Here’s an example of a setter that changes the item name to uppercase. To re-render the custom element when the setter is called, create a private reactive property. In this example, the private reactive property is itemNameUpper. This property is set in the setter and
returned by the getter. Annotate either the getter or the setter with @api, but not both. It’s a best practice to annotate the getter. If you declare a getter and a setter with @api decorators, don’t declare the public property explicitly.

 

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

export default class Todoitem extends LightningElement {
     @api
     get itemName() {
         return this._itemName;
     }

     set itemName(value) {
         this._itemName = value.toUpperCase();
     }
}

 

Push the changes and now you can able to see the output as shown below

 

Private Reactive Properties

To observe a component’s internal state and rerender when its state changes, decorate a property with @track. If the value of a tracked
property changes and it’s referenced in the template, the component rerenders. Tracked properties are also called private reactive
properties. The tracked property can be referenced directly in the template, or it can be used indirectly in a getter and setter.

Let’s look at some sample code to see how @track works.

You can use @track to decorate a property only; you can’t use it to decorate an object. It’s possible to decorate multiple properties
with @track. A property can have only one decorator. For example, a property can’t have both @track and @api.
Let’s look at some sample code to see how @track works.

Create a lightning web component using the below SFDX command.

sfdx force:lightning:component:create --type lwc --componentname child --outputdir force-app\main\default\lwc

Use the below child.html

<template>
    <div>Child value: {itemName}</div>
</template>

 

Use the below child.js code

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

export default class Child extends LightningElement {
    @track itemNameUpper='Hello';
    @api
    get itemName() {
        return this.itemNameUpper;
    }
    set itemName(value) {
        this.itemNameUpper = value.toUpperCase();
    }
}

 

Use the below child.js-meta.xml

 

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

 

In this example, the parent component consumes the child component. When you click the parent component, it updates
a tracked counter property, which causes the component to rerender.

sfdx force:lightning:component:create --type lwc --componentname parent --outputdir force-app\main\default\lwc

Use below parent.html code

<template>
    <div onclick={handleClick} class="listing">
        <div>
            Click to increment.<br>
            Expected: parent and child values update<br>
            {counter}
        </div>
        <br>
        <div>Parent changingGetter: {changingGetter}</div>
        <div>Parent changingVar: {changingVar}</div>
        <br>
        <c-child item-name={changingGetter}></c-child>
        <c-child item-name={changingVar}></c-child>
    </div>
</template>

Use below parent.js code

 

import {
    LightningElement,
    track
} from 'lwc';

export default class Parent extends LightningElement {
    @track counter = 1;
    changingVar = 'initial changingVar';
    get changingGetter() {
        return this.counter + ' changingGetter';
    }
    handleClick() {
        this.counter += 1;
        this.changingVar = this.counter + ' changingVar';
    }
}

Use below parent.js-meta.xml

 

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

 

To see how tracked properties cause components to rerender. push changes and add this component to the page layouts. you can able to see the template is rerenders every time user click on the div.

 

 

 

 

In parent.js, remove @track from the counter property. Run the code and click the parent component. The parent component doesn’t re-render. The child component doesn’t re-render.  You can’t create a getter or setter for a private reactive property. Since the property is private, you can access or modify it directly in the code so a getter or setter isn’t needed. Update the code as below

import {
    LightningElement,
    track
} from 'lwc';

export default class Parent extends LightningElement {
   counter = 1;
    changingVar = 'initial changingVar';
    get changingGetter() {
        return this.counter + ' changingGetter';
    }
    handleClick() {
        this.counter += 1;
        this.changingVar = this.counter + ' changingVar';
    }
}

 

Push the changes to scratch org and now when you click on the div , the counter changes wnt be referlct to the UI and template wnt reredners .

Styling Lightning web components

Let us discuss here what are the different ways we can able to style the lightning web component.we will be discussing here how to use the SLDS framework and custom styles and Shadow DOM. To give your component the Lightning Experience look and feel, use Lightning Design System. To go your own way, use CSS. Here is the simple apex class we will be using to render the data in UI.

 

public with sharing class AccountController {

@AuraEnabled(cacheable=true)
public static List<Account> getAllAccounts() {
    return [SELECT Id, Name,Rating,Phone  FROM Account];
}

}

 

Create a lightning web component using the following sfdx command

sfdx force:lightning:component:create --type lwc --componentname cssexample --outputdir force-app\main\default\lwc

and now we will be using this component to test the different ways to apply the styles to LWC

Option 1: Using Lightning Design System

Salesforce Lightning Design System is a CSS framework that provides a look and feels that’s consistent with Lightning Experience. Use Lightning Design System styles to give your custom Lightning web components a UI that is consistent with Salesforce, without having to reverse-engineer our styles. And best of all, it just works with Lightning components running in Lightning Experience and in the Salesforce mobile application. Components in the lightning namespace already use Lightning Design System. They also interoperate with Lightning web components. The Following example shows how are render the table using lightning design system

Use the below cssexample.html code which will be using the SLDS styles for the table.

<template>
    <template if:true={accounts}>
        <table class="slds-table slds-table_cell-buffer slds-table_bordered">
            <thead>
                <tr class="slds-line-height_reset">
                    <th class="" scope="col">
                        <div class="slds-truncate" title="Name">Name</div>
                    </th>
                    <th class="" scope="col">
                        <div class="slds-truncate" title="Phone">Phone</div>
                    </th>
                </tr>
            </thead>
            <tbody>

                <template for:each={accounts} for:item="acc">
                    <tr class="slds-hint-parent" key={acc.Id}>
                        <th data-label="Name" scope="row">
                            <div class="slds-truncate">{acc.Name}</div>
                        </th>
                        <td data-label="Rating">
                            <div class="slds-truncate">{acc.Phone}</div>
                        </td>
                    </tr>
                </template>
            </tbody>
        </table>
    </template>
</template>

Use the below cssexample.js example code

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

import getAllAccounts from '@salesforce/apex/AccountController.getAllAccounts';
export default class Cssexample extends LightningElement {
    @track accounts;
    @track errors;
    @wire(getAllAccounts)
    wireAllAccs({
        error,
        data
    }) {
        if (data) {
            this.accounts = data;
        } else {
            this.error = error;
        }
    }
}

Use the below cssexample.js-meta.xml

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

Push changes to scratch org and add this component to the layout .you can able to see the output as shown below and look and feel is inherited from the lightning design system as shown below.

 

 

 

Option 2: Custom CSS Style Sheet 

To bundle styles with a component, create a style sheet in the component’s folder. The style sheet must have the same name as the component. The style sheet is applied to the component automatically. Each component can have only one style sheet. Components can’t share style sheets. Style sheets use standard CSS syntax and you can use most selectors. Because Lightning web components have a shadow DOM, styles defined in a component’s style sheet are scoped to the component. They don’t apply to a parent, child, or sibling components. This rule is strict, but it allows a component to be reused in different contexts without losing its style. It also prevents a component’s styles from overriding styles in other parts of a page. Refer the below code that will use custom CSS styles

<template>
    <template if:true={accounts}>
        <table class="tablecls">
            <tr>
                <th>
                    <div title="Name">Name</div>
                </th>
                <th>
                    <div title="Phone">Phone</div>
                </th>
            </tr>
            <template for:each={accounts} for:item="acc">
                <tr key={acc.Id}>
                    <td data-label="Name">
                        <div>{acc.Name}</div>
                    </td>
                    <td data-label="Rating">
                        <div>{acc.Phone}</div>
                    </td>
                </tr>
            </template>
        </table>
    </template>
</template>

Use the below CSS code.

.tablecls {
    font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
    border-collapse: collapse;
    width: 100%;
}

.tablecls td,
.tablecls th {
    border: 1px solid #ddd;
    padding: 8px;
}

.tablecls tr:nth-child(even) {
    background-color: #f2f2f2;
}

.tablecls tr:hover {
    background-color: #ddd;
}

.tablecls th {
    padding-top: 12px;
    padding-bottom: 12px;
    text-align: left;
    background-color: #4CAF50;
    color: white;
}

You can able to see the output as shown below.

Another important way to style a component, create a style sheet in the component bundle with the same name as the component. The style sheet is applied automatically. Lightning web components have a shadow DOM, styles defined in a component’s style sheet are scoped to the component. They don’t apply to a parent, child, or sibling components. This rule is strict, but it allows a component to be reused in different contexts without losing its style. It also prevents a component’s styles from overriding styles in other parts of a page.

This example demonstrates how the CSS styles defined in a parent component don’t reach into the shadow DOM of a child. There are two components, c-cssparent and c-csschild, which each contains an <h1> tag. The cssparent.css style sheet defines the h1 style as xx-large. When you run the code, the style applies only to the <h1> tag in the parent, not to the <h1> tag in the nested child

Create a lightning web component using the below SFDX command.

sfdx force:lightning:component:create --type lwc --componentname csschild --outputdir force-app\main\default\lwc

Use the below csschild.html

<template>
    <h1>To Do Item (h1)</h1>
</template>

 

Create another lighting web component using the below SFDX command.

 sfdx force:lightning:component:create --type lwc --componentname cssparent --outputdir force-app\main\default\lwc

Use the below cssparent.html

<template>
    <h1>To Do List (h1)</h1>
    <c-childcss></c-childcss>
</template>

 

Use the below cssparent.css

h1 {
    font-size: xx-large;
}

 

now push the changes and add them to the page layout and you can able to see the output as shown below.

 

A parent component can style a child component, but it styles it as a single element. A parent can’t reach into a child to style elements inside its shadow DOM. Let’s add a c-csschild selector to cssparent.css that defines a border around the child component.

update the cssparent.css styles as shown below

h1 {
    font-size: xx-large;
}

c-csschild {
    display: block;
    border: 2px solid red;
}

Push changes and you can able to see the values as shown below.

Now let’s style the componentc-csschild from its own style sheet, csschild.css. Before we add a selector to csschild.css, let’s remove the c-csschild selector from cssparent.css to remove the red box. It’s not a good practice to style components from both the component and its parent, because it can be confusing and yield unexpected results. Here is cssparent.css with the c-csschild removed.

updat ethe parent.css like below and see the changes

h1 {
    font-size: xx-large;
}

 

Option 3: Style the Host Element

Because Lightning web components have a Shadow DOM, you can use the standard :host selector to style the host element. CSS styles applied to the host element don’t affect child components or parent components. To style the host element, use the :host selector.

:host {
display: block;
background-color: palevioletred;
}

The :host selector accepts an optional list of selectors. To match, the host element must have a class that matches the passed selector.

/* Match host element if it has "active" class */
:host(.active) {
background-color: rebeccapurple;
}
/* Match host element if it has "foo" or "bar" class */
:host(.foo, .bar) {
background-color: firebrick;
}

 

create the lwc component using the below command

 

sfdx force:lightning:component:create --type lwc --componentname hello --outputdir force-app\main\default\lwc

hello.html code

<template>
    <div>
        Styles from the host ..
    </div>
</template>

hello.css

:host {
    display: block;
    background-color: palevioletred;
}

 

you can able to see the output as shown below

 

 

Option 4: Use Aura Design Tokens

 

Let’s discuss her how to use the Aura Design token for the lightning web components. Design tokens are named entities that store visual design attributes, such as margins and spacing values, font sizes and families, or hex values for colors. Design tokens must be defined in an Aura component in the same DOM as your Lightning web component. Then, the Lightning web component can use CSS variable syntax to reference the Aura component’s design token. When you reference a design token, use the –lwc- prefix. For example, imagine a parent Aura component with these design tokens.

 

Create a new Aura Component token defaulttoken.css

<aura:tokens>
    <aura:token name="myBodyTextFontFace"
                value="Salesforce Sans', Helvetica, Arial"/>
    <aura:token name="myBodyTextFontWeight" value="bold"/>
</aura:tokens>

 

then you can able to refer this token in lighting web component using the below syntax.

 
div {
font-family: var(--lwc-myBodyTextFontFace, "sans-serif");
}

 

create a simple lwc using the below SFDX command  that  will be using the Aura design token

 

sfdx force:lightning:component:create --type lwc --componentname csstest --outputdir force-app\main\default\lwc

 

use the below csstest.html

<template>
    <div>
        This is Demo of CSS using tokens

    </div>
</template>

 

update the csstest.css  as below

div {
    font-family: var(--lwc-myBodyTextFontFace, "sans-serif");
    font-variant: var(--lwc-myBodyTextFontWeight, "bold");

}

 

use the below csstest.js-meta.xml

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

 

create an aura component as shown below which is act like contains for the lightning web components and we will be inheriting the Aura tokens styles from this Aura component to Lightning web component.

<aura:component implements="flexipage:availableForAllPageTypes"  access="global" >
	<c:csstest></c:csstest>
</aura:component>

 

push the changes and  Add it to the page layout you can able to see the below styles which are inherited from the Aura tokens.

 

 

 

Overall these are the different way you can apply styles to lightning web components

  1.  Using Lightning Design System
  2. Custom CSS
  3. Using Host Selector
  4. Using Aura Tokens

 

Lightning Web Component First Example

Let’s discuss here the complete end to end application using the Lightning web components. This example, we will be seeing the application that will be used to maintain the issue log. The app which we will be building looks like below.  Please refer to this link for environment setup.

 

1. Create an apex class 

 

Create an Apex class that will fetch the list of issues from the database. This class fetches the list of issues from the database.

public with sharing class IssueController {
    @AuraEnabled(cacheable=true)
    public static List<Issue__c> getAllIssues() {
       return [SELECT Id, Name, Assigned_To__c, Description__c,Priority__c,Resolved__c FROM Issue__c Order by Name];
    }
}

2.Create an Issue Log LWC

Create a lightning component using the below SFDX command

This component will do

  • Insert the new record into the issue log using wire services
  • Fire an event with the newly created record.
  • Show toast message on insert the new record.
sfdx force:lightning:component:create --type lwc --componentname issuelog --outputdir force-app\main\default\lwc 

Use this code in issuelog.html

<template>
    <lightning-card title="Log Issue" icon-name="standard:record">
        <div class="slds-m-around_medium">
            <lightning-input label="Issue Description" type="text" onchange={handleChange} class="slds-m-bottom_x-small"></lightning-input>

            <lightning-combobox name="Priority" label="Priority" value="" placeholder="Select Priority"
             options={options}
                onchange={handleChange}></lightning-combobox>

            <lightning-button label="Log Issue" variant="brand" onclick={createIssue}></lightning-button>
        </div>
    </lightning-card>
</template>

Use this code in issuelog.js

import {
    LightningElement,
    track,
    wire
} from 'lwc';
import {
    ShowToastEvent
} from 'lightning/platformShowToastEvent';
import {
    createRecord
} from 'lightning/uiRecordApi';
import ISSUE_OBJECT from '@salesforce/schema/Issue__c';
import ASSIGNED_FIELD from '@salesforce/schema/Issue__c.Assigned_To__c';
import DESCRIPTION_FIELD from '@salesforce/schema/Issue__c.Description__c';
import PRIOITY_FIELD from '@salesforce/schema/Issue__c.Priority__c';

export default class Issuelog extends LightningElement {
    @track description;
    @track proirity;
    get options() {
        return [{
                label: 'Low',
                value: 'Low'
            },
            {
                label: 'Medium',
                value: 'Medium'
            },
            {
                label: 'High',
                value: 'High'
            },
            {
                label: 'Critical',
                value: 'Critical'
            },
        ];
    }
    handleChange(event) {
        if (event.target.label === 'Issue Description') {
            this.description = event.target.value;
        }
        if (event.target.label === 'Priority') {
            this.proirity = event.target.value;
        }
    }
    createIssue() {
        const fields = {};
        fields[DESCRIPTION_FIELD.fieldApiName] = this.description;
        fields[PRIOITY_FIELD.fieldApiName] = this.proirity;

        const recordInput = {
            apiName: ISSUE_OBJECT.objectApiName,
            fields
        };
        createRecord(recordInput)
            .then(issue => {
                console.log('issue' + JSON.stringify(issue));
                const event = new CustomEvent('newrecord', {
                    detail: {
                        data: issue
                    },
                });
                this.dispatchEvent(event);
                console.log('event fired');
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Success',
                        message: 'Issue Logged with Record ',
                        variant: 'success',
                    }),
                );
                this.description = '';
            })
            .catch(error => {
                console.log('error' + JSON.stringify(error, null, 2));

                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error creating record',
                        message: error.message,
                        variant: 'error',
                    }),
                );
            });
    }
}

 

Understand the issuelog.js

 

The below get method will be returning the lits of the options for combo box .insted of passing the hardcoded values to get method you can able to use the getPicklist Values wired Adapter

get options() {
        return [{
                label: 'Low',
                value: 'Low'
            },
            {
                label: 'Medium',
                value: 'Medium'
            },
            {
                label: 'High',
                value: 'High'
            },
            {
                label: 'Critical',
                value: 'Critical'
            },
        ];
    }

The below code will handle the change event from the description and priority fields changes.

 handleChange(event) {
        if (event.target.label === 'Issue Description') {
            this.description = event.target.value;
        }
        if (event.target.label === 'Priority') {
            this.proirity = event.target.value;
        }
    }

The following code in the create issue method will be used to save the record and will be passed to createRecord wire adapter

const fields = {};
        fields[DESCRIPTION_FIELD.fieldApiName] = this.description;
        fields[PRIOITY_FIELD.fieldApiName] = this.proirity;

        const recordInput = {
            apiName: ISSUE_OBJECT.objectApiName,
            fields
        };

createRecord will be used to save the record and in the same method, we will firing an event with a newly created record using the new CustomEvent.

 createRecord(recordInput)
            .then(issue => {
                console.log('issue' + JSON.stringify(issue));
                const event = new CustomEvent('newrecord', {
                    detail: {
                        data: issue
                    },
                });
                this.dispatchEvent(event);

the following code will be used to dispatch the toast message to UI when the record is saved of failed

 

 this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Success',
                        message: 'Issue Logged with Record ',
                        variant: 'success',
                    }),
                );

 

 

Use this issuelog.js-meta.xml file to configure the issue log.

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

</LightningComponentBundle>

 

3.Create an Issue List LWC

create a lightning component using the following sfdx command.

This component will be

  1. Container component for the issue log component
  2. Received an event that is the trigger by issue log component
  3. handler the resolved checkbox checked and unchecked and navigation logic.

 

sfdx force:lightning:component:create --type lwc --componentname issuelist --outputdir force-app\main\default\lwc 

 

Use this issuelist.html code

<template>
    <lightning-card title="Add New Issue ">
        <c-issuelog onnewrecord={handleNewRecord}></c-issuelog>
    </lightning-card>
    <lightning-card title="List of Issues">
        <div class="slds-m-horizontal_large">
            <template if:true={issues}>
                <template if:true={issues}>
                    <table class="slds-table slds-table_cell-buffer slds-no-row-hover slds-table_bordered">
                        <thead>
                            <tr class="slds-line-height_reset">
                                <th class="" scope="col">
                                    <div class="slds-truncate" title="Name">Name</div>
                                </th>
                                <th class="" scope="col">
                                    <div class="slds-truncate" title="Description">Description</div>
                                </th>
                                <th class="" scope="col">
                                    <div class="slds-truncate" title="Priority">Priority</div>
                                </th>
                                <th class="" scope="col">
                                    <div class="slds-truncate" title="Resolved">Resolved</div>
                                </th>
                            </tr>
                        </thead>
                        <tbody>
                            <template for:each={issues} for:item="issue">

                                <tr key={issue.Id}>
                                    <td data-label="Name">
                                        <a value={issue.Id} data-recordid={issue.Id} onclick={handleNameClicked}>
                                            {issue.Name}</a>
                                    </td>
                                    <td data-label="Description"> {issue.Description__c}</td>
                                    <td data-label="Priority"> {issue.Priority__c}</td>
                                    <td data-label="Resolved">
                                        <lightning-input data-recordid={issue.Id} type="checkbox" checked={issue.Resolved__c}
                                            value={issue.Resolved__c} onchange={handleChecked}></lightning-input>

                                    </td>
                                </tr>
                            </template>
                        </tbody>
                    </table>
                </template>
                <template if:false={issues.length}>
                    No Data Found
                </template>
            </template>
            <template if:true={error}>
                Error While Loading data from Salesforce
            </template>
        </div>
    </lightning-card>
</template>

use this code in issuelist.js

import {
    LightningElement,
    wire,
    api,
    track
} from 'lwc';
import {
    ShowToastEvent
} from 'lightning/platformShowToastEvent';

import {
    createRecord,
    generateRecordInputForUpdate,
    updateRecord,
    deleteRecord,
} from 'lightning/uiRecordApi';

import {
    refreshApex,
    getSObjectValue
} from '@salesforce/apex';

import getAllIssues from '@salesforce/apex/IssueController.getAllIssues';

import {
    NavigationMixin
} from 'lightning/navigation';


export default class Issuelist extends NavigationMixin(LightningElement) {
    @track issues;
    @track error;
    wiredIssueResult;
    @track recordId;
    @api currentRecord
    @wire(getAllIssues)
    wiredIssue(result) {
        this.wiredIssueResult = result;
        if (result.data) {
            this.issues = result.data;
            this.error = undefined;
        } else if (result.error) {
            this.error = result.error;
            this.issues = undefined;
        }
    }


    handleNewRecord(evt) {
        return refreshApex(this.wiredIssueResult);
    }
    handleChecked(event) {
        event.preventDefault();
        const recId = event.target.dataset.recordid;
        let record = {
            fields: {
                Id: recId,
                Resolved__c: event.target.checked,
            },
        };
        updateRecord(record)
            .then(() => {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Success',
                        message: 'Record is updated',
                        variant: 'success',
                    }),
                );
            })
            .catch(error => {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error while updating record',
                        message: error.message,
                        variant: 'error',
                    }),
                );
            });

    }
    handleNameClicked(event) {
        event.preventDefault();
        const recId = event.target.dataset.recordid;
        this[NavigationMixin.Navigate]({
            type: 'standard__recordPage',
            attributes: {
                recordId: recId,
                objectApiName: 'Issue__c',
                actionName: 'view'
            },
        });

    }

}

 

understand the code

The following code is used to handle the event that is received from the issue log .here I am simply refreshing the wire adapter which will get the updated values

 handleNewRecord(evt) {
        return refreshApex(this.wiredIssueResult);
    }

You can able to handle the event values as shown below as well and get the event values and parse it.

this.issues = [
    ...this.issues,
    {
        "Id": evt.detail.data.id,
            "Name": evt.detail.data.fields.Name.value,
            "Assigned_To__c": evt.detail.data.fields.Assigned_To__c.value,
            "Description__c": evt.detail.data.fields.Description__c.value,
            "Priority__c": evt.detail.data.fields.Priority__c.value,
            "Resolved__c": evt.detail.data.fields.Resolved__c.value
    },
];

 

The following code will be used to update the values when user click on the checkbox resolved

    handleChecked(event) {
        event.preventDefault();
        const recId = event.target.dataset.recordid;
        let record = {
            fields: {
                Id: recId,
                Resolved__c: event.target.checked,
            },
        };
        updateRecord(record)
            .then(() => {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Success',
                        message: 'Record is updated',
                        variant: 'success',
                    }),
                );
            })
            .catch(error => {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error while updating record',
                        message: error.message,
                        variant: 'error',
                    }),
                );
            });

    }

 

The following code is used to fire the navigation event

 handleNameClicked(event) {
        event.preventDefault();
        const recId = event.target.dataset.recordid;
        this[NavigationMixin.Navigate]({
            type: 'standard__recordPage',
            attributes: {
                recordId: recId,
                objectApiName: 'Issue__c',
                actionName: 'view'
            },
        });

    }

 

 

 

Use the below issuelist.js-meta.xml code

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

</LightningComponentBundle>

 

4. Push changes

Push the changes to scratch org using the below SFDX command and add this component to the layout

 

 sfdx force:source:push --forceoverwrite

 

Here is the final output of the code.

 

 

 

Making Conditional Required In Lightning Web Component

Lets discuss here how to make the field as required in conditionally in lightning web components. In this example, we will be making the field as required conditionally if the selected value is hot.

 

Create a lightning web component using the following SFDX command

sfdx force:lightning:component:create --type lwc --componentname conditionalrequired --outputdir force-app\main\default\lwc

 

Here is the conditionalrequired.html code. In this code, we will be showing the picklist values and based on the selected picklist value we are making the conditional required.

<template>
    <lightning-card title="Conditional Required" icon-name="standard:calibration">
        <div class="slds-form-element">
            <template if:true={ratings.data}>
                <div class="slds-combobox_container slds-size_small">
                    <select onchange={handleOnChange}>
                        <template for:each={ratings.data.values} for:item="item">
                            <option key={item.label}>{item.label}</option>
                        </template>
                    </select>
                </div>
            </template>
            <lightning-input type="text" name="inputtext" label="Enter Input" value="text" required={makerequired}></lightning-input>
            <template if:true={ratings.error}>
                {errors}
            </template>
        </div>
    </lightning-card>
</template>

 

 

Use the below conditionalrequired.js code

import {
    LightningElement,
    wire,
    api
} from 'lwc';
import {
    getPicklistValues
} from 'lightning/uiObjectInfoApi';
import RATING from '@salesforce/schema/Account.Rating';
export default class Conditionalrequired extends LightningElement {
    @api selectVal;
    @wire(getPicklistValues, {
        recordTypeId: '012000000000000AAA',

        fieldApiName: RATING
    })
    ratings;



    handleOnChange(event) {
        this.selectVal = event.target.value;
    }
    get makerequired() {
        if (this.selectVal === "Hot") {
            return true;
        } else {
            return false;
        }
        return ;
    }
}

 

use this conditionalrequired.js-meta.xml code to configure this component to app builder

 

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

 

Push the changes to scratch and add them to page layout.

 

 sfdx force:source:push --forceoverwrite

 

You can able to see the output as shown below.