Maps and Filters in a LWC component, Part 3
This final post builds on the 2 post in the series where we are returning some apex from the controller, using the map function to modify the presented data and then presenting some detail. The main advantage of a datatable being that all the data of the row is available to show additional details, even if the data is not shown to users.
This set of code will show a list of names and then when an entry is selected we will show the details. In this scenario we cannot store the details in the component.
So taking the code from Part 2 lets build a list instead of a component.
So lets change things to present a combobox instead. We are looking to set the options to an array called accountOptions. This will hold the label that is displayed and then the value will be the Id of the record:
<template>
<div style="width: 100%">
<div class="slds-grid slds-gutters">
<div class="slds-col slds-size_7-of-12">
<lightning-card title="Maps and Filters">
<div class="slds-m-around_small">
<div style="height: 600px" class="slds-m-around_x-small">
<lightning-combobox
name="progress"
label="Status"
placeholder="Select Account"
options={accountOptions}
onchange={handleListChange}
></lightning-combobox>
</div>
</div>
</lightning-card>
</div>
<div class="slds-col slds-size_5-of-12">
<lightning-card title="Account Details">
<div><b>Rating :</b>{detailRating}</div>
<div><b>Phone :</b>{detailPhone}</div>
<div><b>Type :</b>{detailType}</div>
<div><b>Industry :</b>{detailIndustry}</div>
<div><b>Employees :</b>{detailEmployees}</div>
</lightning-card>
</div>
</div>
</div>
</template>
Our javascript code is more complex, in that we need to use the map as we did before to create the additional fields (so we can display the full address for instance) and use the map again to form the options for the combobox:
import { LightningElement, wire } from "lwc";
import getAccountList from "@salesforce/apex/mapsandfiltersController.getAccountList";
export default class MapsAndFilters extends LightningElement {
accountList;
accountOptions;
detailRating;
detailPhone;
detailType;
detailIndustry;
detailEmployees;
@wire(getAccountList)
getAccountList({ error, data }) {
if (data) {
console.log("--getAccountList v2--");
console.log(data);
const modifiedData = data.map((item, index) => {
let completeBillingAddress = [];
if (item.BillingAddress) {
if (item.BillingAddress.street != null) {
completeBillingAddress.push(item.BillingAddress.street);
}
if (item.BillingAddress.city != null) {
completeBillingAddress.push(item.BillingAddress.city);
}
if (item.BillingAddress.city != null) {
completeBillingAddress.push(item.BillingAddress.city);
}
if (item.BillingAddress.state != null) {
completeBillingAddress.push(item.BillingAddress.state);
}
if (item.BillingAddress.postalCode != null) {
completeBillingAddress.push(item.BillingAddress.postalCode);
}
}
let addressQuality = "";
if (item.BillingAddress) {
addressQuality = item.BillingAddress.postalCode
? "Good"
: "No Postal Code";
} else {
addressQuality = "No address provided";
}
return {
...item,
CompleteBillingAddress: completeBillingAddress.join(", "),
AddressQuality: addressQuality
};
});
console.log(JSON.stringify(modifiedData));
this.accountList = modifiedData;
const accountOptions = data.map((item, index) => {
return {
label: item.Name,
value: item.Id
};
});
console.log(JSON.stringify(accountOptions));
this.accountOptions = accountOptions;
} else if (error) {
console.log("--error getAccountList --");
console.log(error);
}
}
handleListChange(event) {
console.log("handleListChange");
const selectedItem = event.detail.value;
console.log(JSON.stringify(selectedItem));
}
}
From the code above this is the extract that is creating the options for the combobox:
const accountOptions = data.map((item, index) => {
return {
label: item.Name,
value: item.Id
};
});
console.log(JSON.stringify(accountOptions));
this.accountOptions = accountOptions;
We are returning a completely new object and so we don’t use …item as we only want the label and value.
For the time being our handleListChange will only find the Id of the record we need. When we collected the detailed list we have already seen the detail of each of the records, so there is no need to make another call back to the server. What we need to do is to go back to our accountList and find the record with the ID we need and then look up the information.
This is where the filter JavaScript method comes in (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter). This will extract out a new array of only the results we need – for us an array of 1 item (but importantly an array).
The code below will look through the accountList and if the Id matches our selectedItem then the item is added to the new array.
handleListChange(event) {
console.log("handleListChange");
const selectedItem = event.detail.value;
console.log(JSON.stringify(selectedItem));
const filterAccountList = this.accountList.filter(
(elem) => elem.Id == selectedItem
);
}
With our new filterAccountList of 1 array item we can set the details as we did in Part 2:
handleListChange(event) {
console.log("handleListChange");
const selectedItem = event.detail.value;
console.log(JSON.stringify(selectedItem));
const filterAccountList = this.accountList.filter(
(elem) => elem.Id == selectedItem
);
this.detailRating = filterAccountList[0].Rating;
this.detailPhone = filterAccountList[0].Phone;
this.detailType = filterAccountList[0].Type;
this.detailIndustry = filterAccountList[0].Industry;
this.detailEmployees = filterAccountList[0].NumberOfEmployees;
}
The filterAccountList[0] is required as the filter command returns an array. So now when an item is selected the details that we hold on the client side (so no need to go back to the server) are shown to the user.
The use of filterAccountList[0] is a bit unclear and so it perhaps better to use what is called Destructuring Assignment (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) to allocate our 1 item array to a single variable and this is achieved by the following:
const [filterAccount] = filterAccountList;
This takes the array filterAccontList and maps the first item to filterAccount. This means we can use filterAccount:
handleListChange(event) {
console.log("handleListChange");
const selectedItem = event.detail.value;
console.log(JSON.stringify(selectedItem));
const filterAccountList = this.accountList.filter(
(elem) => elem.Id == selectedItem
);
const [filterAccount] = filterAccountList;
this.detailRating = filterAccount.Rating;
this.detailPhone = filterAccount.Phone;
this.detailType = filterAccount.Type;
this.detailIndustry = filterAccount.Industry;
this.detailEmployees = filterAccount.NumberOfEmployees;
}