I'm trying to pass the new data from a parent LWC to a child. The data is a list of wrapped objects with the following properties: string, string, list, list.
I'm able to send and it renders perfect, but when I click the get data button again the last retrieved data is not rendering or updating the child component.
This is what I have tried.
- Method 1: using
@api set and get
- Method 2: exposing the child controller method to parents and sending data through parameters.
Method 1
Tried to use @api set and get
for the property as described here
Parent HTML
<template if:true={objectRecordsMap}>
<c-custom-datatable
object-records-map={objectRecordsMap}
oncustom_event={handleCustomEvent}
></c-custom-datatable>
</template>
<lightning-button
class="slds-var-m-left_medium"
variant="brand"
label="Get Records"
title="Get records for this contact"
onclick={searchRecordsForContact}
></lightning-button>
Parent JS
objectRecordsMap;
searchRecordsForContact() {
returnRecordsForContact({
contactId: this.theContactId
})
.then((results) => {
this.objectRecordsMap = results;
this.errors = [];
})
.catch((error) => {
this.objectRecordsMap = undefined;
});
this.checkForErrors();
}
Child HTML
<template if:true={records}>
<template for:each={records} for:item="record">
<div class="slds-var-m-top_medium" key={record.key}>
<lightning-card icon-name={record.iconName} title={record.objectPluralLabel}>
<div class="slds-card__body slds-card__body_inner">
<c-rich-datatable
key-field={record.key}
data={record.recordList}
columns={record.columns}
onrowaction={handleRowAction}
show-row-number-column
enable-infinite-loading
onrowselection={handleRowSelection}
>
</c-rich-datatable>
</div>
</lightning-card>
</div>
</template>
</template>
Child JS
@api set objectRecordsMap(value) {
this.records = [...value]
}
get objectRecordsMap() {
return this.records;
}
records;
connectedCallback() {
if (this.objectRecordsMap.length !== 0) {
this.buildRecordsDatatables();
}
}
buildRecordsDatatables() {
let _map = this.objectRecordsMap;
let newCol = { type: 'action', typeAttributes: { rowActions: actions } };
for (let _key in _map) {
if (_map.hasOwnProperty(_key)) {
let record = {
key: _key,
iconName: _map[_key].iconName,
objectPluralLabel: _map[_key].objectPluralLabel,
recordList: _map[_key].recordList
};
let cols = [];
_map[_key].columns.forEach(element => {
cols.push(element);
});
cols.push(newCol);
record.columns = cols;
this.records.push(record);
}
}
}
Which results and renders in this:
First search (set of data)
Second search.
Console log:
Method 2
Tried to use @api exposedMethod(param)
to call the child method imperatively as described here
Parent HTML
<c-retire-contact-datatable
onreplace_contact_for_selected_records={handleReplace}
></c-retire-contact-datatable>
Parent JS
objectRecordsMap;
searchRecordsForContact() {
returnRecordsForContact({
retireContactId: this.contactToRetire
})
.then((results) => {
this.objectRecordsMap = results;
let datatableCmp = this.template.querySelector('c-retire-contact-datatable');
datatableCmp.buildRecordsDatatables(this.objectRecordsMap);
this.errors = [];
})
.catch((error) => {
this.errors.push({ message: error.message.body });
this.notifyUser('Error', error.message.body, 'error');
this.objectRecordsMap = undefined;
});
this.checkForErrors();
}
Child HTML
<template>
<template for:each={records} for:item="record">
<div class="slds-var-m-top_medium" key={record.key}>
<lightning-card icon-name={record.iconName} title={record.objectPluralLabel}>
<lightning-button-icon
icon-name="utility:change_record_type"
slot="actions"
label="Replace Contact for selected records"
title="Replace Contact for selected records"
alternative-text="Replace Contact for selected records"
onclick={handleReplaceContactClick}
></lightning-button-icon>
<div class="slds-card__body slds-card__body_inner">
<c-icrm-rich-datatable
key-field={record.key}
data={record.recordList}
columns={record.columns}
onrowaction={handleRowAction}
show-row-number-column
enable-infinite-loading
onrowselection={handleRowSelection}
>
</c-icrm-rich-datatable>
</div>
</lightning-card>
</div>
</template>
</template>
Child JS
records = [];
@api buildRecordsDatatables(wrappedData) {
let _map = wrappedData;
let newCol = { type: 'action', typeAttributes: { rowActions: actions } };
for (let _key in _map) {
// eslint-disable-next-line no-prototype-builtins
if (_map.hasOwnProperty(_key)) {
let record = {
key: _key,
iconName: _map[_key].iconName,
objectPluralLabel: _map[_key].objectPluralLabel,
recordList: _map[_key].recordList
};
let cols = [];
_map[_key].columns.forEach(element => {
cols.push(element);
});
cols.push(newCol);
record.columns = cols;
this.records.push(record);
}
}
}
But this way is not rendering the datatables in the child component, but is does process the child method:
I think I'm pretty close with the second method since it's processing the data as I need it.
Best Answer
the push method does not trigger a
mutation
therefore, the data table is not re-rendered in the ui with the newly fetched data (even though you can see in your logs that a new row has been added)I would suggest you use map or use a spread operator to copy the changed data to your
records
property.