I am trying to return a list of users through a search box to be used in a distributed marketing implementation.
I have a container component that houses the search box component and the iteration that will iterate through the results and display the result component.
What's weird, is that this code works fine when pushed to a scratch org, but when pushed into a PRD org it's throwing an error:
Assert Violation: Invalid template iteration for value
Search Component
<template>
<lightning-card title="Search for an Agent">
<div class="slds-m-around_medium">
<lightning-input
type="search"
onchange={handleKeyChange}
class="slds-m-bottom_small"
label="Search"
value={searchKey}
></lightning-input>
</div>
</lightning-card>
</template>
Search JS
import { LightningElement, wire } from "lwc";
import getAgents from "@salesforce/apex/AgentSelectorController.getAgents";
/** The delay used when debouncing event handlers before invoking Apex. */
const DELAY = 300;
export default class AgentSelectorContainer extends LightningElement {
searchKey = "";
agentResults;
error;
@wire(getAgents, { searchKey: "$searchKey" })
wiredAgents({ error, data }) {
if (data) {
// despatch an event to pass agent results into parent component
const agentEvent = new CustomEvent("agents", {
detail: this.agentResults
});
// eslint-disable-next-line no-console
console.log("about to dispatch " + JSON.stringify(this.agentResults));
this.dispatchEvent(agentEvent);
} else if (error) {
this.error = error;
this.agentResults = undefined;
}
}
// TODO - change this to despatch data as an event
// function to handle the dealy and the keystrokes
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 searchKey = event.target.value;
// eslint-disable-next-line @lwc/lwc/no-async-operation
this.delayTimeout = setTimeout(() => {
this.searchKey = searchKey;
}, DELAY);
}
}
Container Component
<template>
<!-- Agent Selector component -->
<c-agent-selector onagents={handleOnAgents}></c-agent-selector>
<!-- Results Heading -->
<template if:true={agentResults}>
<c-results-heading></c-results-heading>
<lightning-layout multiple-rows="true">
<template for:each={agentResults} for:item="agentResult">
<lightning-layout-item
size="12"
key={agentResult.Id}
padding="around-small"
>
<c-agent-result
record-id={agentResult.Id}
onselected={handleOnSelected}
></c-agent-result>
</lightning-layout-item>
</template>
</lightning-layout>
</template>
</template>
Container JS
import { LightningElement, track } from "lwc";
export default class SelectAgentContainer extends LightningElement {
@track agentResults;
// listen for an event from the search and populate agentResults
handleOnAgents(event) {
this.agentResults = event.detail;
}
handleOnSelected(event) {
// this function generates the event to pass to the container aura component
// that will handle the distributed marketing logic. This will simply pass
// the email and name of the agent that was clicked on.
event.preventDefault();
// eslint-disable-next-line no-console
console.log(
"received selected event in lwc container: " +
JSON.stringify(event.detail)
);
const sendAgentDetail = new CustomEvent("sendagent", {
detail: event.detail
});
this.dispatchEvent(sendAgentDetail);
}
}
Controller Apex
public with sharing class AgentSelectorController {
// controller for the agent selector search box
@AuraEnabled(cacheable=true)
public static List<User> getAgents(String searchKey) {
String searchString = '%' + searchKey + '%';
List<User> agents = new List<User>();
if (searchString.length() > 0) {
agents = [
SELECT Id, FirstName, LastName, Email
FROM User
WHERE
(FirstName LIKE :searchString
OR LastName LIKE :searchString)
AND isActive = true
WITH SECURITY_ENFORCED
];
}
return agents;
}
}
Help me obi wan kenobi… 😛
Best Answer
You haven't assigned
agentResults
with any value from the Search JS. You need to assign wired result data into it. See below code.