2

I have a parent and a child component below is the code:

Parent component HTML:

<template>
    <lightning-tabset>
        <lightning-tab label="Child One">
            <c-child-one record-id={sobjectId}></c-child-one>
        </lightning-tab>
        <lightning-tab label="Child Two">
        </lightning-tab>
        <lightning-tab label="Child Three">
        </lightning-tab>
    </lightning-tabset>
</template>

Parent Component JS:

import {LightningElement, api, wire} from 'lwc';
import getRecordId from '@salesforce/apex/DemoCTRL.getRecordId'

export default class ChildOne extends LightningElement {
    @api recordId;
    sobjectId;

    constructor() {
        super();
        console.log('Constructor Called in Parent');
    }

    //getting dependent record Id
    @wire(getRecordId, {recordId: '$recordId'}) getSobjectId({error, data}) { 
        if(data) {
            console.log('Wired Called In Parent');
            this.sobjectId= data;
        }
        else if (error) {
            console.log(error);
        }
    }
}

Child Component HTML:

<template>
    <table class="sobject-table slds-table slds-table_cell-buffer slds-table_bordered">
        <thead>
            <tr class="slds-line-height_reset">
                <template for:each={fieldSetData} for:item="field">
                    <th key={field.apiName} title={field.fieldLabel} class="" data-scope="col">
                        <div class="slds-truncate" title={field.fieldLabel}>{field.fieldLabel}</div>
                    </th>
                </template>
            </tr>
        </thead>
        <tbody>
            <template for:each={records} for:item="row">
                <tr key={row} class="slds-hint-parent">
                    <template for:each={row} for:item="data">
                        <td key={data.id} data-label="Name" data-scope="row">{data.value}</td>
                    </template>
                </tr>
            </template>
        </tbody>
    </table>
</template>

Child Component JS:

import {LightningElement, api, wire} from 'lwc';
import {processRecords} from 'c/recordGenerator';
import getFieldSetData from '@salesforce/apex/FieldSetData.getFieldSetData';
import getSobjectRecords from '@salesforce/apex/DeomoCTRL.getRecords';
import VanillaDatatable from '@salesforce/resourceUrl/VanillaDatatable';
import {loadScript, loadStyle} from 'lightning/platformResourceLoader';

export default class ManageSelectedIndicators extends LightningElement {
    @api recordId;
    records;
    fieldSetData;
    @wire(
        getFieldSetData,
        {objectName: 'SOBJECTName', fieldSet: 'FIELSETName'}
    ) wiredFieldSetData({error, data}) {
        console.log('wired called in Child')
        if (data) {
            this.fieldSetData = data;
            this.getData();
        }
    }


    constructor() {
        super();
        console.log('Constructor called in Child');
    }

    connectedCallback() {
        console.log('Connected callback');
    }

    renderedCallback() {
        console.log('Rendered callback');
        // Promise.all([
        //     loadScript(this, VanillaDatatable + '/VanillaDataTables/src/vanilla-dataTables.js')
        // ]).then(() => {
        //     const table = new DataTable(
        //         document.getElementsByClassName('.project-indicator'),
        //         {searchable: true, sortable : true}
        //     );
        // })
        // .catch(error => {
        //    console.log(error);
        // });
    }

    getData() {
        getSobjectRecords ({
            sobjectId: this.recordId
        })
        .then(response => {
            this.records= processRecords(response, this.fieldSetData);
        })
        .catch((error) => {
            console.log(error);
        });
    }

}

Here's the result after the page has loaded:

enter image description here

I'm not sure what's going on here and as a result of that my table is not rendering with data (only table headers are rendered because fieldSetData is populated) and I'm unable to instantiate the new DataTable() JS because the table doesn't exist.

Edit: looks like the sobjectId only gets populated after the child has rendered and hence the records is empty.

Please recommend the best approach to get the record in the parent -> pass it to the child component -> load related records -> render the table -> then call the JS

3
  • 1
    You would typically ensure that values used in your template are reactive; if you make "records" tracked I suspect things will render correctly.
    – Phil W
    Feb 27 '20 at 8:23
  • @PhilW I have tried that but the behaviour is very inconsistent. Is there anything else that I'm doing wrong in my code?
    – d_k
    Feb 27 '20 at 8:48
  • Since in the order of execution for child cmp, wired is fired before connecttedCallback is called, have you tried calling getData() in connectedCallback? Feb 27 '20 at 9:07
7
@wire(
    getFieldSetData,
    {objectName: 'SOBJECTName', fieldSet: 'FIELSETName'}
)

This wire in child is getting called even before you get recordId from parent and so you cannot say when will getSobjectRecords be invoked and when its response is returned, whether or not fieldSet information is available or not.

fixes:

Option 1. Render child only when sobjectId is available: (preferred)

    <c-child-one if:true={sobjectId} record-id={sobjectId}></c-child-one>

Option 2.

    @wire(
        getFieldSetData,
        {objectName: 'SOBJECTName', fieldSet: 'FIELSETName', recordId: '$recordId'}
    )

Make above wire dependent on recordId. This will get data only if it gets valid recordId

Added based on comments:

if:true is like normal javascript if condition which checks if value is:

  1. not null
  2. not undefined
  3. not blank
  4. is true if boolean
5
  • Will if:true={sobjectId} work as in the code example above right now? Or do we you need a doesSObjectIdExist getter method in the JS code? Feb 27 '20 at 9:13
  • 1
    @BrianMiller added in answer Feb 27 '20 at 9:22
  • Fantastic - I didn't know the if:true worked like a regular JS condition check Feb 27 '20 at 12:40
  • I believe is:true will also return false if it's an empty array Oct 13 '20 at 17:36
  • @MayTheSForceBeWithYou as its just like regular JS function, what you said is correct - [] and {} are not really empty/null/undefined. and so it returns false Oct 14 '20 at 6:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.