Tuesday, May 12, 2020

Adding and removing rows to a custom table using lightning web component

below example illustrates how can we add new row to a lightning table and how can we delete a table row, and how can we save table.

createComponentReq.html

<template>  
    <div class="slds-m-around--xx-large container-fluid">
        <div class="slds-float_right slds-p-bottom_small">
            <lightning-button label="Add" onclick={addRow} icon-name="utility:add"></lightning-button>
        </div>
        <div class="container-fluid">
            <table class="slds-table slds-table_bordered slds-table_cell-buffer">
                <thead>
                    <tr class="slds-text-title_caps">
                        <th scope="col">
                            <div class="slds-truncate">#</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Field Label">Field Label</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Field API Name">Field API Name</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Field Data Type">Field Data Type</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Object Name">Object Name</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Object Relation">Object Relation</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Action">Action</div>
                        </th>
                    </tr>
                </thead>
                <tbody>
                    <template for:each={RequestList} for:item="req" for:index="indx">
                        <tr key={req.key} id={req.key}>
                            <td>{indx}</td>
                            <td>
                                <lightning-input data-id={indx} label="Field Label" value={req.Label}
                                    onblur={handleLabelChange}></lightning-input>
                            </td>
                            <td>
                                <lightning-input data-id={indx} label="Field Api Name" value={req.ApiName}
                                    onblur={handleApiChange}></lightning-input>
                            </td>
                            <td>
                                <lightning-combobox data-id={indx} label="Field Data Type" value={req.DataType}
                                    placeholder="-Select-" options={DataTypePicklistValues.data.values}
                                    onblur={handleDataTypeChange}>
                                </lightning-combobox>
                                <!--
                                <lightning-input data-id={indx} label="Field Data Type" value={req.DataType}
                                    onchange={handleDataTypeChange}></lightning-input> -->
                            </td>
                            <td>
                                <lightning-input data-id={indx} label="Object Api Name" value={req.ObjectName}
                                    onblur={handleObjectNameChange}></lightning-input>
                            </td>
                            <td>
                                <lightning-input data-id={indx} label="Object Relation" value={req.ObjectRelation}
                                    onblur={handleObjectRelationChange}></lightning-input>
                            </td>
                            <td>
                                <lightning-button-icon icon-name="utility:delete" data-id={indx}
                                    alternative-text="Delete" class="slds-m-left_xx-small" onclick={removeRow}
                                    title="Delete"></lightning-button-icon>
                            </td>
                        </tr>
                    </template>
                </tbody>
            </table>
            <div class="slds-align_absolute-center slds-p-top_small">
                <lightning-button name="Save" label="Generate Component" onclick={saveRecord}></lightning-button>
            </div>
        </div>
    </div>
</template>


createComponentReq.js

import { LightningElement, track, api, wire } from 'lwc';
import { getPicklistValues } from 'lightning/uiObjectInfoApi';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import REQUEST_OBJECT from '@salesforce/schema/Dynamic_Request__c';
import LABEL_FIELD from '@salesforce/schema/Dynamic_Request__c.Field_Label__c';
import API_FIELD from '@salesforce/schema/Dynamic_Request__c.Field_Api_Name__c';
import DATATYPE_FIELD from '@salesforce/schema/Dynamic_Request__c.Field_Data_Type__c';
import OBJECT_FIELD from '@salesforce/schema/Dynamic_Request__c.Object_Name__c';
import OBJECTRELATION_FIELD from '@salesforce/schema/Dynamic_Request__c.Object_Relationship__c';
import saveRequests from '@salesforce/apex/RequestController.saveRequests';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class CreateDynamicRecord extends LightningElement {
    @track RequestList = [];
    @track index = 0;
    @track label = LABEL_FIELD;
    @track apiName = API_FIELD;
    @track dataType = DATATYPE_FIELD;
    @track objectName = OBJECT_FIELD;
    @track objectRelation = OBJECTRELATION_FIELD;

    @api req = {
        Label: this.label,
        ApiName: this.apiName,
        DataType: this.dataType,
        ObjectName: this.objectName,
        ObjectRelation: this.objectRelation,
        key: ''
    }

    @wire(getObjectInfo, { objectApiName: REQUEST_OBJECT })
    objectInfo;

    @wire(getPicklistValues, { recordTypeId: '$objectInfo.data.defaultRecordTypeId', fieldApiName: DATATYPE_FIELD})
    DataTypePicklistValues;

    addRow() {
        this.index++;
        var i = this.index;
        this.req.key = i;
        this.RequestList.push(JSON.parse(JSON.stringify(this.req)));
   

    removeRow(event) {
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        if (this.RequestList.length > 1) {
            this.RequestList.splice(key, 1);
            this.index--;
        } else if (this.RequestList.length == 1) {
            this.RequestList = [];
            this.index = 0;
        }
    }

    handleLabelChange(event) {
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        this.RequestList[key].Label = event.target.value;
    }

    handleApiChange(event) {
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        this.RequestList[key].ApiName = event.target.value;
    }

    handleDataTypeChange(event){
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        this.RequestList[key].DataType = event.target.value;
    }

    handleObjectNameChange(event){
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        this.RequestList[key].ObjectName = event.target.value;
    }

    handleObjectRelationChange(event){
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        this.RequestList[key].ObjectRelation = event.target.value;
    }   

    saveRecord() {
        console.log('RequestList--->>'+JSON.stringify(this.RequestList));
        saveRequests({ reqList: this.RequestList })
            .then(result => {
                this.message = result;
                this.error = undefined;
                if (this.message !== undefined) {
                    this.req.Lable = '';
                    this.req.ApiName = '';
                    this.req.DataType = '';
                    this.req.ObjectName = '';
                    this.req.ObjectRelation = '';
                    this.dispatchEvent(
                        new ShowToastEvent({
                            title: 'Success',
                            message: 'Request created successfully',
                            variant: 'success',
                        }),
                    );
                }
                console.log('res-->'+JSON.stringify(result));
                console.log("result", this.message);
            })
            .catch(error => {
                this.message = undefined;
                this.error = error;
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error creating record',
                        message: error.body.message,
                        variant: 'error',
                    }),
                );
                console.log("error", JSON.stringify(this.error));
            });
    }
}


createComponentReq.js-meta.xml

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

Friday, May 8, 2020

custom dependent picklist

Creating a custom lightning web component for custom dependent pick-list

I created two custom picklist fields on account object called "TestPick__c" and "TestDepPick__c"
TestPick__c is a controlling field, TestDepPick__c is a dependennt field.

Based on the selection of TestPick__c fields TestDepPcik__c fields will vary.

Below is the component code,

custdepPicklist.html

<template>
    <lightning-card title="Dependent picklist" icon-name="custom:custom14">
        <lightning-layout>
            <lightning-layout-item>
                <div class="slds-m-around_medium">
                    <lightning-combobox label="Controlling picklist" name="cpick" onchange={handleCPicklist}
                        options={controlingpick} placeholder="--None--" value={selectedCpick}></lightning-combobox>
                    <br />
                    <template if:true={selectedCpick}>
                        <p>Controlling Value is, {selectedCpick}</p>
                    </template>
                </div>
            </lightning-layout-item>
            <lightning-layout-item>
                <div class="slds-m-around_medium">
                    <lightning-combobox label="Dependent picklist" name="dpick" onchange={handleDPicklist}
                     options={dependentpick} placeholder="--None--" value={selectedDpick}></lightning-combobox>
                     <br />
                    <template if:true={selectedDpick}>
                        <p>Dependent Pick Value is, {selectedDpick}</p>
                    </template>
                </div>
            </lightning-layout-item>
        </lightning-layout>
    </lightning-card>
</template>



custdepPicklist.js

import { LightningElement,wire } from 'lwc';
import { getPicklistValuesByRecordType } from 'lightning/uiObjectInfoApi';
import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';

export default class CustdepPicklist extends LightningElement {
    controlingpick = [];
    dependentpick = [];
    selectedCpick;
    selectedDpick;
    error;
    isEmpty;
    controllingValues;
    totDependentValues = [];

    // Account object info
    @wire(getObjectInfo, { objectApiName: ACCOUNT_OBJECT })
    objectInfo;

    // Picklist values based on record type
    @wire(getPicklistValuesByRecordType, { objectApiName: ACCOUNT_OBJECT, recordTypeId: '$objectInfo.data.defaultRecordTypeId'})
    primaryPicklistValues({error, data}) {
        if(data) {
            this.error = undefined;
            var controlingpickval = [{label:'--None--', value:'--None--'}];           
            data.picklistFieldValues.TestPick__c.values.forEach(key => {
                controlingpickval.push({
                    label : key.label,
                    value: key.value
                })
            });
            this.controlingpick = controlingpickval;
            var dependentOptions = [{label:'--None--', value:'--None--'}];            
            this.controllingValues = data.picklistFieldValues.TestDepPick__c.controllerValues;           
            this.totDependentValues = data.picklistFieldValues.TestDepPick__c.values;
            this.totDependentValues.forEach(key => {
                dependentOptions.push({
                    label : key.label,
                    value: key.value
                })
            });

            this.dependentpick = dependentOptions;
        }
        else if(error) {
            this.error = JSON.stringify(error);
        }
    }

    handleCPicklist(event) {       
        this.selectedCpick = event.target.value;
        this.isEmpty = false;
        var dependValues = [];

        if(this.selectedCpick) {           
            if(this.selectedCpick === '--None--') {
                this.isEmpty = true;
                dependValues = [{label:'--None--', value:'--None--'}];
                this.selectedCpick = null;
                this.selectedDpick = null;
                return;
            }           
            this.totDependentValues.forEach(conValues => {
                if(conValues.validFor[0] === this.controllingValues[this.selectedCpick]) {
                    dependValues.push({
                        label: conValues.label,
                        value: conValues.value
                    })
                }
            })

            this.dependentpick = dependValues;
        }
    }

    handleDPicklist(event) {
        this.selectedDpick = event.target.value;
    }
}


custdepPicklist.js-meta.xml

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


 

Monday, May 4, 2020

Creating a wrapper class to create Account and Contact using lwc

Creating a wrapper class to create Account and Contact using lwc

I'm assuming there are no other mandatory fields on Account other than Account Name, on contact also firstName and LastName are required, all other fields are not mandatory.

we can do this Account Creation in multiple ways, in below explaple I'm explaining how can we create Account Record first and how can we associate Account with contact using a wrapper class.

Note: Same requirement we can do without using wrapper class also.

accountContactCreationWrapper.html

<template>
    <lightning-card title="Wrapper Example" icon-name="custom:custom14">
        <div class="slds-m-around_medium">
            <lightning-input type="text" label="Account Name" value={accountName} onchange={handleAccountName}>
            </lightning-input>
            <lightning-input type="text" label="First Name" value={firstName} onchange={handlefChange}>
            </lightning-input>
            <lightning-input type="text" label="Last Name" value={lastName} onchange={handlelChange}></lightning-input>
            <br />
            <lightning-button label="Save" icon-name="utility:save" onclick={handleSave}></lightning-button>
            &nbsp;&nbsp;
            <lightning-button label="Cancel" icon-name="utility:clear" onclick={handleCancel}></lightning-button>
        </div>
    </lightning-card>
</template>

accountContactCreationWrapper.js

import { LightningElement, track } from "lwc";
import { ShowToastEvent } from "lightning/platformShowToastEvent";
import wrapperExa from "@salesforce/apex/AccountContactCreationWrapper.insertAccAndCon";
import saveContact from '@salesforce/apex/AccountContactCreationWrapper.ContactCreateion';

export default class AccountContactCreationWrapper extends LightningElement {
  @track accountName = "";
  @track firstName = "";
  @track lastName = "";

  @track record = {
    accName: this.accountName,
    firstName: this.firstName,
    lastName: this.lastName,
  };

  handleAccountName(event) {
    this.record.accName = event.target.value;
  }

  handlefChange(event) {
    this.record.firstName = event.target.value;
  }

  handlelChange(event) {
    this.record.lastName = event.target.value;
  }

  handleSave(event) {
    wrapperExa({ wrp: this.record })
      .then((result) => {  
        console.log("result--->>>" + JSON.stringify(result));
        if(result != "error"){     
          saveContact({conWrap: this.record, accountId: result })
          .then(results =>{
              const contoast = new ShowToastEvent({
                title: "success",
                message: "Contact Created Successfully",
                variant: "success",
              });
              this.dispatchEvent(contoast);
          })
          .catch(error => {
              const conerrortoast = new ShowToastEvent({
                title: "error",
                message: "Error Creating Contact!",
                variant: "error",
              });
              this.dispatchEvent(conerrortoast);
          });
          const toast = new ShowToastEvent({
            title: "success",
            message:  "Record Created Successfully",
            variant: "success",
          });
          this.dispatchEvent(toast);
        }else if(result === "error"){
          const toast = new ShowToastEvent({
            title: "Error",
            message:  "Unable to Create Account Record!",
            variant: "error",
          });
          this.dispatchEvent(toast);
        }      
      })
      .catch((error) => {
        console.log("error--->>>" + JSON.stringify(error));
        const toast = new ShowToastEvent({
          title: "error",
          message: "Failed to insest record",
          variant: "error",
        });
        this.dispatchEvent(toast);
      });
  }

  handleCancel(event) {
    this.template.querySelector('lightning-input').reset();
  }

}

accountContactCreationWrapper.js-meta.xml

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

</LightningComponentBundle>

AccountContactCreationWrapper.cls

public class AccountContactCreationWrapper {
    
    @AuraEnabled
    public static String insertAccAndCon(WrapperCls wrp){
        WrapperCls wt = new WrapperCls();
        wt = wrp;

        Account ac = new Account();
        ac.Name = wt.accName;
        try{
            insert ac;
        }catch(Exception ex){}

        if(ac.Id!=null){
            return ac.Id;
        }            
        else {
            return 'error';   
        }    
    }

    @AuraEnabled
    public static string ContactCreateion(WrapperCls conWrap, String accountId){
        WrapperCls wt = new WrapperCls();
        wt = conWrap;

        Contact con = new Contact();
        con.firstName = wt.firstName;
        con.lastName = wt.lastName;
        if(!String.isBlank(accountId)){
            con.AccountId = accountId;
        }

        try {
            insert con;
        } catch (Exception ex) {}

        if(con.Id!=null){
            return con.Id;
        }else {
            return 'error';
        }
    }

    public class WrapperCls{

        @AuraEnabled
        public String firstName{get;set;}

        @AuraEnabled
        public String lastName{get;set;}

        @AuraEnabled
        public String accName{get;set;}
    }

}




Sunday, May 3, 2020

Writing a calculator program using lwc and commonUtils javaScript class

Create a simple calculator using lwc and commonUtils class

calculator.html

<template>
    <lightning-card title="Calculator Example">
        Calculated value: {results}
        <lightning-input type="number" label="Enter first value" name="firstval" onchange={handleChange}></lightning-input>
        <lightning-input type="number" label="Enter second value" name="secondval" onchange={handleChange}></lightning-input>
        <br />
        <lightning-button-group>
            <lightning-button label="Add" icon-name="utility:add" onclick={handleAddition}></lightning-button>
            <lightning-button label="Subtract" onclick={handleSubtraction}></lightning-button>
            <lightning-button label="Multiply" onclick={handleMultiply}></lightning-button>
        </lightning-button-group>
    </lightning-card>
</template>

calculator.js

import { LightningElement, track } from 'lwc';
import { addition, subtraction, multiplication } from 'c/commonUtils';

export default class Calculator extends LightningElement {
    @track firstvalue = 0;
    @track secondvalue = 0;
    @track results = 0;

    handleChange(event){
        if(event.target.name === "firstval"){
            this.firstvalue = event.target.value;
        }else if(event.target.name === "secondval"){
            this.secondvalue = event.target.value;
        }
    }

    handleAddition(event){
        this.results = addition( parseInt(this.firstvalue),  parseInt(this.secondvalue));
    }

    handleSubtraction(event){
        this.results = subtraction( parseInt(this.firstvalue),  parseInt(this.secondvalue));
    }

    handleMultiply(event){
        this.results = multiplication( parseInt(this.firstvalue),  parseInt(this.secondvalue));
    }
}

calculator.js-meta.xml

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

commonUtils.js


const addition = (firstval, secondval) => {
    if(firstval && secondval > 0){
        return firstval + secondval;
    }
    return 0;
};

const subtraction = (firstval, secondval) => {
    if(firstval && secondval > 0){
        return firstval - secondval;
    }
    return 0;
};

export function multiplication(firstval, secondval){
  return firstval * secondval;
};

export { addition, subtraction };

Monday, April 20, 2020

Lightning Web Components


Lightning web components are custom HTML elements built using HTML and modern JavaScript.
Lightning web components and Aura components can coexist and interoperate on a page.

Setting the environment




Component Folder

  • myTestComponent
  • myTestComponent.html
  • myTestComponent.js
  • myTestComponent.js-meta.xml
  • myTestComponent.css
  • myTestComponent.svg

The folder and its files must follow these naming rules.

  • Must begin with a lowercase letter
  • Must contain only alphanumeric or underscore characters
  • Must be unique in the namespace
  • Can’t include whitespace
  • Can’t end with an underscore
  • Can’t contain two consecutive underscores
  • Can’t contain a hyphen (dash)


HTML File
  • Every UI component must have an HTML file with the root tag <template>
  • When a component renders, the <template> tag is replaced with the name of the component, <namespace-component-name>
Component JavaScript File
  • Every component must have a JavaScript file. If the component renders UI, the JavaScript file defines the HTML element.
  • The JavaScript file follows the naming convention <component>.js, such as myComponent.js. The convention is for the class name to be Pascal Case, where the first letter of each word is capitalized. For myComponent.js, the class name is MyComponent
  • To import a class, function, or variable declared in a module, use the import statement. To allow other code to use a class, function, or variable declared in a module, use the export statement.
  • The core module in Lightning Web Components is lwc. The import statement imports LightningElement from the lwc module.
    import { LightningElement } from 'lwc’;

    export default class MyComponent extends LightningElement {
    }

Component Configuration File
  • Every component must have a configuration file. The configuration file defines the metadata values for the component, including the design configuration for Lightning App Builder and Experience Builder.
  • The configuration file follows the naming convention <component>.js-meta.xml, such as myComponent.js-meta.xml.
  • Include the configuration file in your component’s project folder, and push it to your org along with the other component files.
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>45.0</apiVersion>
    <isExposed>false</isExposed>
    <targets>
         <target>lightning__AppPage</target>
         <target>lightning__RecordPage</target>
         <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

Component CSS File
  • A component can include a CSS file. Use standard CSS syntax to style Lightning web components.
  • To style a component, create a style sheet in the component bundle with the same name as the component. If the component is called myComponent, the style sheet is myComponent.css. The style sheet is applied automatically.
Component SVG Icon
  • A component can include an SVG resource to use as a custom icon in Lightning App Builder and Experience Builder.
  • To include an SVG resource as a custom icon for your component, add it to your component’s folder. It must be named <component>.svg. If the component is called myComponent, the svg is myComponent.svg. You can only have one SVG per folder.