[SalesForce] Assert Violation: Invalid template iteration for value Error in LWC template

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.

@wire(getAgents, { searchKey: "$searchKey" })
wiredAgents({ error, data }) {
    if (data) {
       /*-------- The Below line was missing ------- */
       this.agentResults = 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;
    }
}
Related Topic