I have a lightning component that will display a long list of contacts (could be thousands), with a nested data table from a related object. That related data is editable, so I'll want to be able to update the Contact info of a single contact without doing a SOQL on all the contacts. So, while my component is iterating on a List, I want to create a Map.
COMPONENT
<aura:attribute name="Contacts" type="List" default="[]"/>
<aura:attribute name="ContactsMap" type="Map" default="{}"/>
My Apex controller is returning List as expected, done in batches using OFFSET, triggered by scroller in the Renderer. So, with each batch, I add the 30 newly retrieved records to the Contacts list, and I need to add them to the Map as well.
In my CONTROLLER, I'm saving the getReturnValue to var contacts, and using push.apply to append that list to the existing Contacts list. My problem happens when I try to put the contacts into my map. It gives me this error:
Uncaught Error in $A.getCallback() [ContactsMap.set is not a function] Callback failed: apex://ContactController/ACTION$getContacts
Here's the code:
CONTROLLER
getData : function(cmp, event, helper)
{
var moreData = cmp.get("v.moreData");
if (moreData)
{
var spinner = cmp.find("spinner");
$A.util.toggleClass(spinner, "slds-hide");
var action = cmp.get("c.getContacts");
var offset = cmp.get("v.offset");
action.setParams({
"offset": offset
});
action.setCallback(this, $A.getCallback(function (response) {
var state = response.getState();
if (cmp.isValid() && state === "SUCCESS"){
var contacts=response.getReturnValue();
var ContactList = cmp.get("v.Contacts");
ContactList.push.apply(ContactList, contacts);
cmp.set('v.Contacts', ContactList);
$A.util.toggleClass(spinner, "slds-hide");
var offsetI = parseInt(offset);
offsetI += 30;
cmp.set('v.offset', offsetI.toString());
cmp.set("v.moreData", (!cmp.get("v.totalContacts")<offsetI));
// push to map
var ContactsMap = cmp.get("v.ContactsMap");
for(var i in contacts)
{
ContactsMap.set(contacts[i].Id, contacts[i]);
}
cmp.set("v.ContactsMap", ContactsMap);
} else if (state === "ERROR") {
var errors = response.getError();
console.error(errors);
}
}));
$A.enqueueAction(action);
}
},
What am I missing?
UPDATE: Thanks for the help on this one. In my lightning component, I needed both a map (for inserting updates) and a list (for aura:iteration) of the data. My question was assuming that SOQL returned a list, and then I'd create the map in the JS controller. I ended up reversing that.
My Apex SOQL query returns the Map (which is as simple as getting the list). Then, in my JS controller, I get the list from the map with just a line:
var contactsMap = response.getReturnValue();
var contactsList = Object.values(contactsMap);
ContactsMap.set(contacts[i].Id, contacts[i]);toContactsMap[contacts[i].Id] = contacts[i];should help