Refreshing iterated Lightning Datatables

lightning-web-componentsrefreshapex

I'm having an issue getting my LWC component with multiple datatables to refresh.

Background:

I'm displaying lectures in multiple data tables, organized by date the lecture was created. So, if there are two lectures created on April 21st, and three on April 23rd. There will be 2 data tables, with 2 and 3 lectures in each table respectively.

There is a "New Lecture" button on the main component which allows the user to create a lecture (by selecting a date in a modal). Once the user creates a lecture, I would like this lecture to show up in the data table without a page refresh. Since each data table is organized by date, it's possible that the lecture may go in an existing data table or a new one will have to be created.

My Issue:

The refreshApex method kinda works – If I create a new lecture with a new date, it will create a new table with my lecture. But, if I create a lecture with the same date as an existing datatable, it will not insert this lecture into the table.

My Question:

Should I be trying something different besides refreshApex for my use case? or is this not wired up correctly between my child and parent components?

Objects and Components

  1. Course__c: Main object which where the LWC is displayed.
  2. Lecture__c: Lookup to Course Object. These are the records in the datatables
  3. lectureDates.apxc: Returns the dates the lecture was created
  4. lectureData.apxc Returns the actual data to populate in the data table
  5. CourseComponent.js: A "Parent" component that pulls the dates from apex and iterates through them and displays the lectures in data tables, organized by date. The data tables are each in their own custom component.
  6. LectureDatatable.js: A "Child" component. This custom component is a data table which will display all the courses for each date

CourseComponent.js (Parent)

import { LightningElement } from 'lwc';
import lectureDates from '@salesforce/apex/CourseController.courseDates';
import { refreshApex } from '@salesforce/apex';

export default class CourseComponent extends LightningElement {

@api recordId;
@api createdDate;
wiredLectureDates;

@wire(lectureDates, { recordId: '$recordId'})
displayLectureDates(dates) {
  this.wiredLectureDates = dates;
}

handleNewLecture() {
  createLecture.open({CourseID : this.recordId }).then(result => {
    refreshApex(this.wiredDates);
  }      
  
}

lectureDates.apxc

@AuraEnabled(cacheable=true)
public static List<DateWrapper> lectureDates(Id recordId) {
   Set<Date> dateSet = new Set<Date>();

   List<Lectures__c> lectures = new List<Lectures__c>([
     select title__c, createddate from lectures__c
   ]);

   for (Lectures__c lecture : lectures) {
     dateSet.add(lecture.createddate);
   }

   for (Date lectureDate : dateSet) {
        DateWrapper dateWrapper = new DateWrapper();
        
        dateWrapper.LectureCreatedDate = lectureDate;
        dateWrapper.CourseId = recordId;
        
        dateWrapperList.add(dateWrapper);
    }

    return dateWrapperList;
}

CourseComponent.html (Parent)

<template>
  <div>
   <lightning-button label="New Lecture" onclick={handleNewLecture}> 
   </lightning-button>
  </div>
  <template for:each={wiredLectureDates.data} for:item="date">
    <lightning-card key={date.LectureCreatedDate}>
      <lightning-accordion>
        <lightning-accordion-section name={date.LectureCreatedDate} label={date.LectureCreatedDate}>

         <c-lecture-datatable 
           created-date={date.LectureCreatedDate}
           record-id={date.CourseID}>
         </c-lecture-datatable>

        </lightning-accordion-section>
        </lightning-accordion>
      </div>
    </lightning-card>
   </template>

LectureDatatable.js (Child)

 import lectureData from '@salesforce/apex/CourseController.lectureData';

 export default class LectureDatatable extends LightningElement {
   @api createdDate;
   @api recordId;
   @track columns = [ {label: 'Course Title' }];
   @track lectureList;

   connectedCallback() {
     lectureData({ recordId: this.recordId, createdDate: this.createdDate }).then(result => {
     this.lectureList = result.map(item => {
      let lectureRecord = Object.assign({},item);
      return lectureRecord;
     }
   }
 }

LectureDatatable.html (Child)

<template>
  <lightning-datatable
      data={lectureList} 
      columns={columns}
      key-field="Id"
    </lightning-datatable>
</template>

lectureData.apxc

@AuraEnabled(cacheable=true)
public static List<LectureWrapper> lectureData(Id recordId) {
   List<LectureWrapper> lectureWrapperList = new List<LectureWrapper>();

   List<Lectures__c> lectures = new List<Lectures__c>([
     select title__c from lectures__c
   ]);

   for (Lectures__c lecture : lectures) {
     LectureWrapper lectureWrapper = new LectureWrapper();
     LectureWrapper.Title = lecture__c;

     lectureWrapperList.add(LectureWrapper);
   }

    return lectureWrapperList;
}

Best Answer

LectureDatatable is only querying its data once, when the table first comes into existence (connectedCallback). There's a few ways to fix this, but the easiest fix from where you are would be to toggle the display while you load records.

<template>
  <div>
   <lightning-button label="New Lecture" onclick={handleNewLecture}> 
   </lightning-button>
  </div>
  <template lwc:if={loading}>
    <lightning-spinner alternative-text="Loading..."></lightning-spinner>
  </template>
  <template lwc:else for:each={wiredLectureDates.data} for:item="date">
    <lightning-card key={date.LectureCreatedDate}>
      <lightning-accordion>
        <lightning-accordion-section name={date.LectureCreatedDate} label={date.LectureCreatedDate}>

         <c-lecture-datatable 
           created-date={date.LectureCreatedDate}
           record-id={date.CourseID}>
         </c-lecture-datatable>

        </lightning-accordion-section>
        </lightning-accordion>
      </div>
    </lightning-card>
   </template>
</template>

Then, in your controller:

loading = true;
@wire(lectureDates, { recordId: '$recordId'})
displayLectureDates(dates) {
  this.wiredLectureDates = dates;
  this.loading = !dates.data;
}
handleNewLecture() {
  createLecture.open({CourseID : this.recordId }).then(result => {
    this.loading = true;
    refreshApex(this.wiredLectureDates);
  }      
}

Now, your data will be briefly removed while the new data loads, and then pop back in when it's done--and all your data will be rendered anew.

Related Topic