- Sign up for a Heroku free account at : https://signup.heroku.com
- Create a free GitHub account at : https://github.com/join
- Create a new Salesforce Developer Org: https://developer.salesforce.com/sig
All about Salesforce
Salesforce CTA speaker 2021
Wednesday, 13 October 2021
Getting started with Heroku
Saturday, 4 September 2021
Type ahead search component using Lightning web component(LWC)
Overview
In this tutorial we are going to build a simple TypeAhead Search component using LWC which creates suggestions as soon as someone begins typing into a text box. We will architect the component in such a way that the final product will be configurableImplementation
To implement Type-ahead Searcher we will use lightning datatable to display the search result. in this scope of article i will not explain how to search record within salesforce database.
below 4 parameters you can pass though dynamically using lightning app builder which is defined in metadata xml file.
- header: name of the section
- object: API name of object
- fields: comma separated API name of fields
- viewable: name of exact label for comma separated fields
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <template> <lightning-card title={header} icon-name="custom:custom63"> <div class="slds-m-around_medium"> <c-search-console object={object} fields={fields} ontypeahead={handleObjects}> </c-search-console> <c-mydata-table objects={objects} viewable={viewable}> </c-mydata-table> </div> </lightning-card> </template> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /* eslint-disable no-console */ import { api, LightningElement, track } from 'lwc'; export default class RummageView extends LightningElement { @api header = '' @api object = '' @api viewable; @api fields = [] @track objects; handleObjects(e){ this.objects = e.detail } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <?xml version="1.0" encoding="UTF-8"?> <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"> <apiVersion>47.0</apiVersion> <isExposed>true</isExposed> <targets> <target>lightning__RecordPage</target> <target>lightning__AppPage</target> <target>lightning__HomePage</target> <target>lightningCommunity__Page</target> <target>lightningCommunity__Default</target> </targets> <targetConfigs> <targetConfig targets="lightning__RecordPage"> <property name="header" type="String"/> <property name="object" type="String"/> <property name="fields" type="String"/> <property name="viewable" type="String"/> </targetConfig> <targetConfig targets="lightning__AppPage"> <property name="header" type="String"/> <property name="object" type="String"/> <property name="fields" type="String"/> <property name="viewable" type="String"/> </targetConfig> </targetConfigs> </LightningComponentBundle> |
1 2 3 4 5 6 7 8 9 10 11 | <template> <lightning-input label="Search" value={query} type="search" onchange={handleKeyChange} class="slds-m-bottom_small"> </lightning-input> </template> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | /* eslint-disable no-console */ import { api, LightningElement, track, wire } from 'lwc' import search from '@salesforce/apex/searchcontroller.search' /** The delay used when debouncing event handlers before invoking Apex. */ const DELAY = 500; export default class RummageBar extends LightningElement { @api object = ''; @api fields = ''; @api objects = []; @track query = ''; base = ''; fieldArray = []; @wire(search, { fieldArray: '$fieldArray', base: '$base', query: '$query' }) wiredResult(objects) { console.log( 'RAN WIRE objects =>' ); console.log( JSON.parse( JSON.stringify( objects ) ) ); if (objects.data) { this.objects = [] this.error = false this.dispatchEvent(new CustomEvent('typeahead', { detail: objects.data })) } else if (objects.error) { this.error = objects.error this.dispatchEvent(new CustomEvent('whytherum', { detail: objects.error })) } } handleKeyChange(event) { // Debouncing this method: Do not update the reactive property as long as this function is // being called within a delay of DELAY. This is to avoid a very large number of Apex method calls. window.clearTimeout(this.delayTimeout) const query = event.target.value console.log('query..>'+query); console.log(this.fields) console.log(this.object) if(!query){ this.objects = [] } if(!this.base){ this.base = `SELECT ${this.fields} FROM ${this.object} ` console.log('base url..>'+this.base); } if( ! this.fieldArray.length ){ this.fieldArray = this.fields.split(',') } if(!query || !this.fieldArray.length || !this.base){ return false } // eslint-disable-next-line @lwc/lwc/no-async-operation this.delayTimeout = setTimeout(() => { this.query = query console.log('THIS>QUERY => ${this.query}') }, DELAY) return true } } |
1 2 3 4 5 6 7 8 9 10 11 12 | <template> <template if:true={isdataFound}> <lightning-datatable key-field="id" data={data} columns={columns} max-row-selection="1"> </lightning-datatable> </template> </template> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | /* eslint-disable no-console */ import { api, LightningElement, track } from 'lwc'; export default class ReactiveTable extends LightningElement { @api viewable @track data =[] @track columns = [] @track isdataFound=false @api get objects(){ return this.data } set objects(data){ console.log('data==>'+JSON.stringify(data)); if(data==null||Object.keys(data).length==0){ this.isdataFound=false; this.data = data return } console.log('length-->'+Object.keys(data).length); this.isdataFound=true; const [ record ] = data const viewable = this.viewable.split(',').map(x => x.trim()) console.log('viewable..'+viewable); const fields = Object.keys(record).filter(x => viewable.includes(x)) console.log('fields..'+fields); const columns = fields.map(x => ({ label: this.labeler(x), fieldName: x })) console.log('fields..'+columns); //console.dir( 'ReactiveTable.set.object data => ' ) console.log( JSON.parse( JSON.stringify( {data} ) ) ) this.columns = columns this.data = data } labeler(raw){ const s = raw.replace('__c', '').replace(/_/gi, ' ') return s } } |
Apex Controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public with sharing class searchcontroller { @AuraEnabled(cacheable=true) public static List<sObject> search(List<String> fieldArray, String base, String query) { system.debug('fieldArr'+fieldArray); system.debug('query'+query); system.debug('base'+base); if(base == '' || fieldArray.size() == 0 || query == ''){ return null; } Integer LMT = 10; // -1 for no limit String key = '%' + query + '%'; String search = base; // Build where clause, skip if invalid for(String field : fieldArray){ if('ID' == field.trim().toUpperCase()){ continue; } search += search.indexOf(' WHERE') == -1 ? ' WHERE '+field+' LIKE :key ' : ' OR '+field+' LIKE :key '; } search += LMT > -1 ? ' LIMIT '+LMT+' ' : ''; // System.debug(base); System.debug(fieldArray); System.debug(query); System.debug(search); return Database.query( search ); } } |
Friday, 9 July 2021
Display Bar Chart using Chart.js in Lightning Web Component(LWC)
Use Case
For an an energy utility company , display electric usage information for an customer during notified outage (Event). Usage basically the actual consumption of electric kilo watt(KW) value on every 15 mins interval.
Y Axis will display : Usage(KW)
- The event intervals to be shaded in blue if the demand is below FSL(Target Threshold)
- The event intervals to be shaded in red if the demand is above FSL
Let's talk about how to implement it..?
Source Code:
As a prerequisite we would need chart.js framework to be loaded as static resource , download the the latest version from https://github.com/chartjs/Chart.js and upload as zip.
Now create the component called "barChartDemo"
barChartDemo.html :
1 2 3 4 5 | <template> <div class ='slds-box slds-theme_default custom-background' > <canvas class="chart" width="200" height="100" lwc:dom="manual"></canvas> </div> </template> |
barChartDemo.js :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | /** * @File Name : barChartDemo.js * @Description : dynamic bar chart to display usage data on 15 mins interval * @Author : Swarup satpati * @Modification Log : * Ver Date Author Modification * 1.0 7/2/2020 swarup satpati demo Version **/ import { LightningElement, track, wire,api } from 'lwc'; /*chart.js library*/ import chartjs from '@salesforce/resourceUrl/ChartJsBIP'; import plugins from '@salesforce/resourceUrl/chartjsannotation'; /*To import third-party JavaScript or CSS library, use the platformResourceLoader module.*/ import { loadScript, loadStyle } from 'lightning/platformResourceLoader'; import { ShowToastEvent } from 'lightning/platformShowToastEvent'; import getusage from '@salesforce/apex/UsageData.getUsageData' export default class BarChartDemo extends LightningElement { @api eventname='Event-011166'; @api said='1449702025'; @api eventstarttime; @api eventendtime; @api chartData; @api Data @track fslvalue=75; @track starttime; @track endtime; @api label=[]; @api usage=[]; error; @api chart; chartjsInitialized = false; renderedCallback() { this.init(); console.log('initialized...'+this.chartjsInitialized); if (this.chartjsInitialized) { return; } this.chartjsInitialized = true; } init(){ getusage({ EventName:this.eventname, SAID:this.said}) .then(data => { console.log('****data***'+JSON.stringify(data)); for(let key in data) { //console.log('label.. data'+JSON.stringify(data[key].label)); this.label.push(data[key].label); this.usage.push(data[key].usagekw); } console.log('usage..'+JSON.stringify(this.usage)); console.log('label..'+JSON.stringify(this.label)); Promise.all([ loadScript(this, chartjs + '/Chart.min.js'), loadStyle(this, chartjs + '/Chart.min.css'), loadStyle(this, plugins + '/chartjs-plugin-annotation.min.js'), loadStyle(this, plugins + '/chartjs-plugin-annotation.js') ]) .then(() => { // disable Chart.js CSS injection console.log('draw chart..'+JSON.stringify(this.usage)); window.Chart.platform.disableCSSInjection = true; this.chartjsInitialized = true; this.diaplayChart(); } ) .catch((error) => { this.error = error; }); }) .catch(error => { console.log('The error ', error); }); } diaplayChart(){ var chartColors = { red: 'rgb(255, 99, 132)', blue: 'rgb(54, 162, 235)' }; var horizonalLinePlugin = { afterDraw: function(chartInstance) { var yScale = chartInstance.scales["y-axis-0"]; var canvas = chartInstance.chart; var ctx = canvas.ctx; var index; var line; var style; var yValue; if (chartInstance.options.horizontalLine) { for (index = 0; index < chartInstance.options.horizontalLine.length; index++) { line = chartInstance.options.horizontalLine[index]; if (!line.style) { style = " (169,169,169, .6)"; } else { style = line.style; } if (line.y) { yValue = yScale.getPixelForValue(line.y); } else { yValue = 0; } ctx.lineWidth = 1; if (yValue) { ctx.beginPath(); ctx.moveTo(0, yValue); ctx.lineTo(canvas.width, yValue); ctx.strokeStyle = style; ctx.stroke(); } if (line.text) { ctx.fillStyle = style; ctx.fillText(line.text, 0, yValue + ctx.lineWidth); } } return; }; } }; Chart.pluginService.register(horizonalLinePlugin); console.log('usage kw..>>'+JSON.stringify(this.usage)); console.log('label..>>'+JSON.stringify(this.label)); var ctx = this.template.querySelector(".chart"); var lineChart = new Chart(ctx, { type: 'bar', data: { labels: this.label, datasets: [{ label: 'KW', backgroundColor: [], data: this.usage, borderColor: 'rgba(121, 159, 222, 1)', fill: true, pointBackgroundColor: "#26B99A", pointBorderWidth: 2, pointHoverRadius: 5, pointRadius: 2, bezierCurve: true, pointHitRadius: 10 }] }, options: { legend: { position: 'top', padding: 10, }, animation: { //animateScale: true, //animateRotate: true, duration:0 }, scales: { yAxes: [{ scaleLabel: { display: true, labelString: 'Usage(kW)' } }], xAxes: [{ scaleLabel: { display: true, labelString: 'Timing' } }] }, "horizontalLine": [{ "y": this.fslvalue, "style":"rgba(30,139,195,0.5)", "text": "FSL(KW)" }], responsive: true } }); console.log('chart update..'); var dataset = lineChart.data.datasets[0]; for (var i = 0; i < dataset.data.length; i++) { console.log('value..'+dataset.data[i]); if (dataset.data[i] > this.fslvalue) { //lineChart.data.datasets[0].backgroundColor[i] = chartColors.red; dataset.backgroundColor[i] = chartColors.red; } else{ console.log('in else...'); dataset.backgroundColor[i] = chartColors.blue; //lineChart.data.datasets[0].backgroundColor.push('rgb(54, 162, 235)'); console.log('in else...'); } } lineChart.update(); } } |
Apex Class :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public with sharing class UsageData { @AuraEnabled public static List<DataSet> getUsageData(String EventName,String SAID){ List<Dataset> dataset=new List<dataset>(); DRMI_BIP_Compliance_Report__c CMReport= [ SELECT Id, SA_ID__c, Event_Name__c,isApproved__c,Event_Date__c,Event_KWh__c,Rep_Name__c,Load_Zone__c, Rep_Phone__c, Address__c, City__c, Name,Excess_Energy_Charge__c,FSL_ComplianceReport__c,Firm_Service_Level__c,Total_excess_energy_charge_c__c,Subject_to_Retest__c,Excess_enregy_in_kw__c,(SELECT id, Usage_Data_in_Kw__c,Hour_Ending__c ,Time_Ending__c from DRMI_BIP_Compliance_Report_Usage__r order by Time_Ending__c),(SELECT id ,Charges__c,Incentives__c,Month_Year__c from DRMI_BIP_Compliance_Report_Summary__r ) FROM DRMI_BIP_Compliance_Report__c where Event_Name__c=:EventName and SA_ID__c=:SAID limit 1 ]; for(DRMI_BIP_Compliance_Report_Usage__c usageRow: CMReport.DRMI_BIP_Compliance_Report_Usage__r){ String Tm=usageRow.Hour_Ending__c; decimal kw=usageRow.Usage_Data_in_Kw__c; dataSet.add(new DataSet(Tm ,kw)); } return dataset; } public class DataSet{ public DataSet(String label ,decimal usagekw){ this.label = label ; this.usagekw = usagekw ; } @AuraEnabled public String label {get;set;} @AuraEnabled public decimal usagekw {get;set;} } } |
Code Explanation :
In the JavaScript class file, you need to include below two imports to access the functionality of the third-party JavaScript library.
1 2 3 4 5 | /*chart.js library*/ import chartjs from '@salesforce/resourceUrl/ChartJsBIP'; import plugins from '@salesforce/resourceUrl/chartjsannotation'; /*To import third-party JavaScript or CSS library, use the platformResourceLoader module.*/ import { loadScript, loadStyle } from 'lightning/platformResourceLoader'; |
The loadScript module is used to load the JavaScript file. The method returns a JavaScript promise which you need to resolve in your code. Below is an example of how to use the loadScript method:
1 2 3 4 5 6 7 8 9 10 11 12 13 | Promise.all([ loadScript(this, chartjs + '/Chart.min.js'), loadStyle(this, chartjs + '/Chart.min.css'), loadStyle(this, plugins + '/chartjs-plugin-annotation.min.js'), loadStyle(this, plugins + '/chartjs-plugin-annotation.js') ]) .then(() => { // disable Chart.js CSS injection console.log('draw chart..'+JSON.stringify(this.usage)); window.Chart.platform.disableCSSInjection = true; this.chartjsInitialized = true; this.diaplayChart(); } ) |
For horizontal Line i have registered "horizonalLinePlugin "to draw the reference line in the bar chart.
Hope you are enjoying my post !
Output :
This is just an example to demonstrate how to use chart.js in advanced level .Happy Coding !!
Sunday, 27 June 2021
Parse CSV file using Lightning Web Component(LWC)
Need an functionality to import CSV file and parse those records for further processing. This post will explain how to use LWC component to process CSV in client side without using apex code.
Hope you are going to like this post.
Lets get through the Code :
app.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <!-- * @File Name : app.html * @Description : * @Author : Swarup Satpati * @Last Modified By : Swarup Satpati * @Last Modified On : 6/25/2020, 4:57:24 PM * @Modification Log : * Ver Date Author Modification * 1.0 6/25/2020 Swarup Satpati Initial Version --> <template> <lightning-card title="CSV Uploader" icon-name="custom:custom19"> <form class="slds-form--inline"> <div class="slds-form-element"> <lightning-input type="file" label="Choose File" accept=".csv" onchange={importcsv} multiple> </lightning-input> </div> <div class="slds-form-element slds-text-body_small slds-text-color_error">{filename}</div> <div class="slds-form-element"> <lightning-button label="Import" title="Non-primary action" onclick={readFiles} class="slds-m-left_x-small"></lightning-button> </div> <div style="margin-left:4%" class="slds-form-element"> <template if:true={showLoadingSpinner}> <lightning-spinner alternative-text="Uploading......" size="medium"></lightning-spinner> </template> </div> </form> <br/> <lightning-datatable data={data} columns={columns} key-field="SAID" > </lightning-datatable> </lightning-card> </template> |
app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | /** * @File Name : app.js * @Description : * @Author : Swarup Satpati * @Last Modified By : Swarup Satpati * @Last Modified On : 6/25/2020, 4:57:24 PM * @Modification Log : * Ver Date Author Modification * 1.0 6/25/2020 Swarup Satpati Initial Version **/ import { LightningElement, track, api } from 'lwc'; const COLS = [ { label: 'SA ID', fieldName: 'SAID' }, { label: 'UUID', fieldName: 'UUID'} ]; export default class App extends LightningElement { @track columns = COLS; @track data; @track showLoadingSpinner = false; MAX_FILE_SIZE = 2000000; //Max file size 2.0 MB filesUploaded = []; filename; importcsv(event){ if (event.target.files.length > 0) { this.filesUploaded = event.target.files; this.filename = event.target.files[0].name; console.log(this.filename); if (this.filesUploaded.size > this.MAX_FILE_SIZE) { this.filename = 'File Size is to long to process'; } } } readFiles() { [...this.template .querySelector('lightning-input') .files].forEach(async file => { try { const result = await this.load(file); // Process the CSV here this.showLoadingSpinner = false; console.log(result); // this.processData(result); this.data=JSON.parse(this.csvJSON(result)); console.log('data..'+JSON.parse(this.data)); } catch(e) { // handle file load exception console.log('exception....'); } }); } async load(file) { return new Promise((resolve, reject) => { this.showLoadingSpinner = true; const reader = new FileReader(); // Read file into memory as UTF-8 //reader.readAsText(file); reader.onload = function() { resolve(reader.result); }; reader.onerror = function() { reject(reader.error); }; reader.readAsText(file); }); } //process CSV input to JSON csvJSON(csv){ var lines=csv.split(/\r\n|\n/); var result = []; var headers=lines[0].split(","); console.log('headers..'+JSON.stringify(headers)); for(var i=1;i<lines.length-1;i++){ var obj = {}; var currentline=lines[i].split(","); for(var j=0;j<headers.length;j++){ obj[headers[j]] = currentline[j]; } result.push(obj); } console.log('result..'+JSON.stringify(result)); //return result; //JavaScript object return JSON.stringify(result); //JSON } } |
Explanation:
Have created a CSV with two columns SAID,UUID and prepare almost 4k records for demo purpose. After uploading this CSV once we click on import button component is invoking a method to pass this file to fileReader and in-turn it's invoke another method to parse this file-reader object into JSON format. Finally , we are displaying json data in lightning data table for further processing....... !.
Note: for big file we should be careful to utilize this approach but its work perfectly till 2 mb.
Output:
Thursday, 18 March 2021
Custom Multi select dropdown using LWC
it looks great and work beautifully , Lets enjoy ..!
multiselectdropdown.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <template> <div class="slds-form-element"> <label class="slds-form-element__label" for="combobox-id-5">Relate to</label> <div class="slds-form-element__control"> <div class="slds-combobox_container slds-size_small"> <section id="maindiv" class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-is-open" aria-expanded="true" aria-haspopup="listbox" role="combobox"> <div onclick={handleClick} onmouseleave={handleMouseOut} class="slds-combobox__form-element slds-input-has-icon slds-input-has-icon_right" role="none"> <input type="text" class="slds-input slds-combobox__input slds-has-focus slds-combobox__input-value" id="combobox-id-5" aria-controls="listbox-id-5" role="textbox" placeholder={defaultText} readonly="" /> <span class="slds-icon_container slds-icon-utility-down slds-input__icon slds-input__icon_right"> <lightning-icon icon-name="utility:down" size="x-small" alternative-text="Approved"></lightning-icon> </span> </div> <template if:true={iswindowOpen}> <div onmouseenter={handlemouseOver} onmouseleave={handleMouseLeave} id="listbox-id-5" class="slds-dropdown slds-dropdown_length-5 slds-dropdown_fluid" role="listbox"> <ul class="slds-listbox slds-listbox_vertical" role="presentation"> <template for:each={_options} for:item="option"> <c-list-items key={option.value} value={option.value} label={option.label} selected={option.selected} onselected={handleSelectedClick}></c-list-items> </template> </ul> </div> </template> </section> </div> </div> </div> </template> |
multiselectdropdown.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | import { LightningElement, api, track } from 'lwc'; export default class Multiselectdropdown extends LightningElement { @api options = [{label:'Resource 1',value:'Resource 1',selected:false}, {label:'Resource 2',value:'Resource 2',selected:false}, {label:'Resource 3',value:'Resource 3',selected:false}, {label:'Resource 4',value:'Resource 4',selected:false}, {label:'Resource 5',value:'Resource 5',selected:false}, {label:'Resource 6',value:'Resource 6',selected:false}, {label:'Resource 7',value:'Resource 7',selected:false}, {label:'Resource 8',value:'Resource 8',selected:false}]; @api selectedValues=[]; @api label=' '; @track _options= []; @track iswindowOpen=false; /*Init() call..*/ connectedCallback() { this._options = JSON.parse(JSON.stringify(this.options)); //sort all array items befor display it to ui this._options=this._options.sort(function compare(a,b) { if (a.value == 'All'){ return -1; } else if (a.value < b.value){ return -1; } if (a.value > b.value){ return 1; } return 0; }); } get defaultText(){ if (this.selectedValues.length === 0) { return "Select an option..."; } if (this.selectedValues.length === 1) { return this.selectedValues[0].label; } else{ return this.selectedValues.length+" Options Selected"; } } handleClick(event){ console.log('query selector..'+this.template.querySelector("section")); this.iswindowOpen=true; this.template.querySelector("section").classList.add("slds-is-open"); } handleMouseOut(event){ if(this.iswindowOpen){ return; } this.template.querySelector("section").classList.remove("slds-is-open"); } handleMouseLeave(event){ this.iswindowOpen=false; this.template.querySelector("section").classList.remove("slds-is-open"); } handlemouseOver(event){ this.iswindowOpen=true; } handleSelectedClick(event){ var value; var selected; event.preventDefault(); event.stopPropagation(); const data = event.detail; value = data.value; selected = data.selected; //shift key ADDS to the list (unless clicking on a previously selected item) //also, shift key does not close the dropdown. if (data.shift) { this._options.forEach(function(option) { if (option.value === value) { option.selected = selected === true ? false : true; } }); } else { this._options.forEach(function(option) { if (option.value === value) { option.selected = selected === "true" ? false : true; } else { option.selected = false; } }); // this.closeDropdown(); } this.selectedValues = this.getOptionsArray(); } getOptionsArray(){ var pills = []; this._options.forEach(function(element) { var interator = 0; if (element.selected) { pills.push({label:element.label, name:element.value, key: interator++}); } }); return pills; } } |
Child Component :
listItems.html
1 2 3 4 5 6 7 8 9 10 11 12 | <template> <li onclick={eventHandler} class="slds-listbox__item" role="presentation" > <div class={listStyle} role="option"> <span class="slds-media__figure"> <lightning-icon icon-name="utility:check" size="x-small" alternative-text="selected" class="slds-icon-utility-check slds-current-color slds-listbox__icon-selected slds-icon_container"></lightning-icon> </span> <span class="slds-media__body"> <span class="slds-truncate no-selection" title={label}> {label}</span> </span> </div> </li> </template> |
listItems.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import { LightningElement,api,track } from 'lwc'; export default class ListItems extends LightningElement { @api key = ''; @api value = ''; @api label = ''; @api selected = false; get listStyle() { var initial = ' slds-media slds-listbox__option_plain slds-media_small slds-listbox__option '; return this.selected === true ? initial + ' slds-is-selected ' : initial ; } eventHandler(event) { event.preventDefault(); event.stopPropagation(); console.log('selcted value..'+this.selected); const selectedEvent = new CustomEvent('selected', { detail: {label:this.label,value:this.value,selected:this.selected,shift:event.shiftKey} }); this.dispatchEvent(selectedEvent); } } |
Explanation..
in code we are using Shift key to select multiple options from dropdown. on select option(s) , events is getting fired from child component and being handled on parent component
# handleSelectedClick(event)
one tricky part is handling showing and hiding of drop down list using below two events:
- onmouseenter
- onmouseleave
OutPut :
Lets see the output and hope you will like it.
Wednesday, 30 September 2020
ESlint in LWC
Before we jump into "ESlint" let me explain about "linter"
what is linter ?
A linter is a tool that identifies issues in your code. Running a linter against your code can tell you many things:
- if the code adheres to a certain set of syntax conventions
- if the code contains possible sources of problems
- if the code matches a set of standards you or your team define
It will raise warnings that you, or your tools, can analyze and give you actionable data to improve your code. ESLint is basically a linter for the JavaScript programming language, written in Node.js
ESlint for LWCIn salesforce ESlint is available by default with the Lightning Web Components extension .Salesforce provides specific ESLint rules out of the box for you as a Lightning Web Component developer so that you can write great code. And if you make a mistake, the linting rules help you see it before you deploy your code.
probably you may remember when create a new project, in lwc metadata folder there is file called ".eslintrc" which define the salesforce specific linting rules.
you may have experienced with the Restricted async operation 'setTimeOut' in LWC. lets take a look into below code snippet which is restricted by ESlint due to performance impact.
1 2 3 4 5 | connectedCallback() { setTimeout(() => { this.variable = true; }, 3000); } |
The ESLint for Lightning Web Components will complain about setTimeout and setInterval. if you wanted to use it for any reason you can stop this by adding a comment before the line of code .
1 2 3 4 5 6 | // eslint-disable-next-line @lwc/lwc/no-async-operation connectedCallback() { setTimeout(() => { this.variable = true; }, 3000); } |
Hope you find this post informative ..
-
Overview In this tutorial we are going to build a simple TypeAhead Search component using LWC which creates suggestions as soon as someon...
-
Use case : we can use sales force standard "Path" component to build chevron on record page showed as below. ...
-
In lightning web component(LWC) we don't need to call server to retrieve record type details. There is a native adapter available to r...
Getting started with Heroku
I am familiar with the heroku for quite long time, have seen lots of people are interested with it but not sure from where its need to Start...