Start tracking your progress
Trailhead Home
Trailhead Home

Work with Lists of Records

Create the Bear List Component

Park rangers want to see a directory of bears directly from their home page. You have been tasked with implementing that bear list.

  1. In VS Code, right-click the lwc folder and click SFDX: Create Lightning Web Component.
  2. Name the component bearList.
  3. Edit the bearList.js-meta.xml file, and replace <isExposed>false</isExposed> with these lines.
    <isExposed>true</isExposed>
    <targets>
    	<target>lightning__AppPage</target>
    	<target>lightning__RecordPage</target>
    	<target>lightning__HomePage</target>
    </targets>
    This enables your component to be placed on any type of page in Lightning App Builder.
  4. Replace the contents of bearList.html with:
    <template>
    	<lightning-card title="Bears" icon-name="utility:animal_and_nature">
    		<div class="slds-card__body_inner">
    			<!-- Start bear list -->
    			<template if:true={bears}>
    				<lightning-layout multiple-rows="true" pull-to-boundary="small">
    					<template for:each={bears} for:item="bear">
    						<lightning-layout-item key={bear.Id} size="3" class="slds-var-p-around_x-small">
    							<!-- Start bear tile -->
    							<lightning-card title={bear.Name} class="bear-tile">
    								<div class="slds-var-p-horizontal_small bear-tile-body">
    									<div class="slds-media">
    										<div class="slds-media__figure">
    											<img src={appResources.bearSilhouette} alt="Bear profile" class="bear-silhouette"/>
    										</div>
    										<div class="slds-media__body">
    											<p class="slds-var-m-bottom_xx-small">{bear.Sex__c}</p>
    											<p class="slds-var-m-bottom_xx-small">{bear.Age__c} years old</p>
    											<p class="slds-var-m-bottom_xx-small">{bear.Height__c} cm</p>
    											<p class="slds-var-m-bottom_xx-small">{bear.Weight__c} Kg</p>
    										</div>
    									</div>
    								</div>
    							</lightning-card>
    							<!-- End bear tile -->
    						</lightning-layout-item>
    					</template>
    				</lightning-layout>
    			</template>
    			<!-- End bear list -->
    			<!-- Data failed to load -->
    			<template if:true={error}>
    				<div class="slds-text-color_error">
    					An error occurred while loading the bear list
    				</div>
    			</template>
    		</div>
    	</lightning-card>
    </template>
    Code highlights:
    • The template tag with the for:each and for:item directives is used to iterate over the bears records. Each iteration item is passed to the bear property.
    • Each iteration of the template is marked with a special key attribute. key must have a unique value in the context of the iteration. That is the bear id in our component.
  5. Replace the contents of bearList.js with:
    import { LightningElement } from 'lwc';
    import ursusResources from '@salesforce/resourceUrl/ursus_park';
    /** BearController.getAllBears() Apex method */
    import getAllBears from '@salesforce/apex/BearController.getAllBears';
    export default class BearList extends LightningElement {
    	bears;
    	error;
    	appResources = {
    		bearSilhouette: `${ursusResources}/img/standing-bear-silhouette.png`,
    	};
    	connectedCallback() {
    		this.loadBears();
    	}
    	loadBears() {
    		getAllBears()
    			.then(result => {
    				this.bears = result;
    			})
    			.catch(error => {
    				this.error = error;
    			});
    	}
    }
    Code highlights:
    • We import the ursusResources adapter, which gives us access to a static resource associated with our app. We use this adapter to build an appResources object that exposes the bear silhouette image URL in the template.
    • We import the getAllBears adapter, which allows us to interact with the BearController.getAllBears() Apex method. The BearController class is bundled in the code that you deployed at the beginning of this project. The getAllBears method returns the result of a query that fetches all bear records.
    • We implement the connectedCallback function, which allows us to execute code after the component is loaded. We use this function to call the loadBears function.
    • The loadBears function calls the getAllBears adapter. The adapter calls our Apex code and returns a JS promise. We use the promise to either save the returned data in the bears property or to report errors. Retrieving data using this approach is called imperative Apex.
  6. Create a new bearList.css file in the bearList directory and paste the following code into the file.
    .bear-tile {
    		border: 1px solid #d2955d;
    		border-radius: .25rem;
    		display: block;
    }
    .bear-silhouette {
    		height: 100px;
    }
    These CSS rules add a border to bear cards and sets the height of a bear silhouette image.
  7. Deploy the updated code to the org. Right-click the default folder and click SFDX: Deploy Source to Org.

Add the Bear List Component to the App Home Page

Let’s add our new component to the app home page.

  1. Back in your org, from the App Launcher ( App Launcher), find and select Ursus Park.
  2. Click Setup ( Setup Gear) and select Edit Page.
  3. Under Custom Components, find your bearList component and drag it to the top left of the page.
  4. Click Save and then Back to return to the home page and check your work.

Bear list on the Ursus Park Home page

Use Wired Apex

Now we’re going to explore a new approach for retrieving the list of bears. We are going to use wired Apex instead of imperative Apex.

  1. Edit bearList.html and replace <template if:true={bears}> with <template if:true={bears.data}>.
  2. Replace <template for:each={bears} for:item="bear"> with <template for:each={bears.data} for:item="bear">.
  3. Replace <template if:true={error}> with <template if:true={bears.error}>. At this point the template is mostly the same other than the imperative Apex. We are now accessing the list of bears by calling bears.data instead of just bears, and we are now retrieving errors with bears.error instead of just error.
  4. Replace the contents of bearList.js with:
    import { LightningElement, wire } from 'lwc';
    import ursusResources from '@salesforce/resourceUrl/ursus_park';
    /** BearController.getAllBears() Apex method */
    import getAllBears from '@salesforce/apex/BearController.getAllBears';
    export default class BearList extends LightningElement {
    	@wire(getAllBears) bears;
    	appResources = {
    		bearSilhouette: `${ursusResources}/img/standing-bear-silhouette.png`,
    	};
    }
    We’ve greatly simplified the JS code by decorating our bears property with wired Apex. All the data we need is now coming through this single line: @wire(getAllBears) bears;
  5. Deploy the updated code to the org and see that it behaves in the same way than with imperative Apex.

Pass Parameters in Your Apex Calls

The number of inhabitants in Ursus Park is on the rise. Rangers want to be able to filter the bear list to quickly find them. Let’s add a search bar to our bear list to help them.

  1. Edit bearList.html and add the following code after the <template if:true={bears.data}> tag.
    <lightning-input type="search"
    	onchange={handleSearchTermChange}
    	variant="label-hidden"
    	class="slds-var-m-bottom_small"
    	label="Search"
    	placeholder="Search for bears"
    	value={searchTerm}>
    </lightning-input>
    This adds a search input field. When its value changes, we call the handleSearchTermChange function.
  2. Add the following code after the </lightning-layout> closing tag.
    <!-- No bears found -->
    <template if:false={hasResults}>
    	<div class="slds-align_absolute-center slds-var-m-vertical_small">
    		This is beary disturbing, we did not find results...
    	</div>
    </template>
    This adds a message indicating no results were found. This message is shown only when the hasResults expression is false.
  3. Replace the contents of bearList.js with:
    import { LightningElement, wire } from 'lwc';
    import ursusResources from '@salesforce/resourceUrl/ursus_park';
    /** BearController.searchBears(searchTerm) Apex method */
    import searchBears from '@salesforce/apex/BearController.searchBears';
    export default class BearList extends LightningElement {
    	searchTerm = '';
    	@wire(searchBears, {searchTerm: '$searchTerm'})
    	bears;
    	appResources = {
    		bearSilhouette: `${ursusResources}/img/standing-bear-silhouette.png`,
    	};
    	handleSearchTermChange(event) {
    		// Debouncing this method: do not update the reactive property as
    		// long as this function is being called within a delay of 300 ms.
    		// This is to avoid a very large number of Apex method calls.
    		window.clearTimeout(this.delayTimeout);
    		const searchTerm = event.target.value;
    		// eslint-disable-next-line @lwc/lwc/no-async-operation
    		this.delayTimeout = setTimeout(() => {
    			this.searchTerm = searchTerm;
    		}, 300);
    	}
    	get hasResults() {
    		return (this.bears.data.length > 0);
    	}
    }
    Code highlights:
    • We add a searchTerm reactive property and we pass it as a parameter of our wired Apex call to searchBears.
    • The handleSearchTermChange function is used to react to changes in the value of the search input field. We purposefully introduce a 300 millisecond delay when updating the searchTerm reactive property. If an update is pending, we cancel it and reschedule a new one in 300 ms. This delay reduces the number of Apex calls when the user is typing letters to form a word. Each new letter triggers a call to handleSearchTermChange but ideally, searchBears is only called once when the user has finished typing. This technique is called debouncing.
    • We expose the hasResults expression to check whether our search has returned bears.
  4. Deploy the updated code to the org and check that the search works with or without results.

Filtered bear list on the Ursus Park Home page

That’s it for this step. We have seen how to handle record lists with imperative Apex then with wired Apex, and we learned how to pass parameters in our Apex calls. Our component’s code has grown significantly in the process, so now let’s break it into subcomponents to make it easier to maintain.