0

I have a 4 layers LWC component: great-grandparent > grandparent > parent > child. I want to pass an @api property from great-grandparent to display and modify data in child.

Because the property does not display on the parent component so please excuse me for posting the code of grandparent and parent only.

Great-Grandparent Controller

@AuraEnabled
    public static List<Sobject> getDatas(Boolean isRecentlyView, 
                                         List<String> fields, 
                                         String sobjectType, 
                                         String query, 
                                         Integer limitNum){
        try {
            if (limitNum != null) {
                if (isRecentlyView) {
                    query = 'SELECT ' + String.join(fields, ', ') + ' FROM ' + sobjectType + ' WHERE LastViewedDate !=null ORDER BY LastViewedDate DESC ';
                    query += ' LIMIT :limitNum';
                } else {
                    query = query.split('FROM')[1];
                    query = 'SELECT ' + String.join(fields, ', ') + ' FROM ' +  query;
                    query += ' LIMIT :limitNum';
                }
                return Database.query(query);
            } else return new List<SObject>();
        } catch (Exception e) {
            throw e;
        }
    }

Great-Grandparent HTML

<c-grandparent data={parentdata}></c-grandparent>

Great-Grandparent JS

import { LightningElement, api, track, wire } from 'lwc';
import getDatas from '@salesforce/apex/ListviewController.getDatas';

export default class Great-Grandparent LightningElement {

@api limitNum; // receiving value from config xml

@track isLoading = false;
@track fieldcolumns = [];

parentdata = [];
numberOfRecords = '';
isRecentlyView = false;

handleGetData(){
    getDatas({
       isRecentlyView: this.isRecentlyView,
       fields: fields,
       sobjectType: this.sObjectType,
       query: query,
       limitNum: this.numberOfRecords})
       .then(result => {
            console.log(result);
            this.parentdata = result;})
       .catch(e => {
            console.error(e);})
     }
}

Grandparent HTML

<c-parent records={data}
   onlistitemdrag={handleListItemDrag}
   onitemdrop={handleItemDrop}>
</c-parent>

Grandparent JS

@api data // I have not performed any logic for it here, so I only post the property

Parent HTML

<template>
    <ul ondrop={handleDrop}
        ondragover={handleDragOver}
        style="height:70vh; overflow-y:auto;">
        <template for:each={records} for:item="recordItem">
            <c-child record={recordItem} 
                key={recordItem.Id}
                onitemdrag={handleItemDrag}>
            </c-child>
        </template>
    </ul>
</template>

Parent JS

import { LightningElement, api } from 'lwc';

export default class Parent extends LightningElement {
    @api records;

    renderedCallback() {
        console.log("Render call back in parent"); //I log to see how it render
        console.log('this.records',this.records);
    }

    handleDragOver(evt){
        evt.preventDefault()
    }

    handleDrop(){
        //some logic here
        this.dispatchEvent(event)
    }

    handleItemDrag(evt){
        //some logic here
        this.dispatchEvent(event)
    }

}

=== I tried another way to approach it by update enforces this read-only behavior ===

Parent HTML Modified Code

<template>
    <ul ondrop={handleDrop}
        ondragover={handleDragOver}
        style="height:70vh; overflow-y:auto;">
        <template for:each={_records} for:item="recordItem">
            <c-child record={recordItem} 
                key={recordItem.Id}
                onitemdrag={handleItemDrag}>
            </c-child>
        </template>
    </ul>
</template>

Parent JS Modified Code

import { LightningElement, api } from 'lwc';

export default class Parent extends LightningElement {
    _records = [];

    get records() {
        return this._records;
    }
    @api set records(value) {
        this._records = value;
    }

    renderedCallback() {
        console.log("Render call back in parent"); //I log to see how it render
        console.log('this._records',this._records);
    }

    handleDragOver(evt){
        evt.preventDefault()
    }

    handleDrop(){
        //some logic here
        this.dispatchEvent(event)
    }

    handleItemDrag(evt){
        //some logic here
        this.dispatchEvent(event)
    }
}

Result from debug console

this.records 
Proxy(Array)
[[Handler]]: ReadOnlyHandler
[[Target]]: Array(5)
length: 5
[[Prototype]]: Array(0)
[[IsRevoked]]: false

Thank you so much for taking a look at my code

2
  • 1
    What's the relevant code for the great-grandparent? Depending on that's written, it may explain why parent is having problems.
    – sfdcfox
    Aug 15, 2023 at 18:40
  • Hi @sfdcfox, I posted additional code for Great-Grandparent. Thank you
    – Pam
    Aug 16, 2023 at 0:26

1 Answer 1

1
numberOfRecords = '';

Isn't a valid number, so your Apex Code won't parse it correctly, and end up with a null value.

if (limitNum != null) {
  // ...
} else return new List<SObject>();

Since limitNum is null here, you'll return an empty list.

Thus, you don't get any results, and nothing appears at the parent level.

To fix this, provide a valid number.

numberOfRecords = 100;

Further, you probably want to use connectedCallback() to call your method, rather than putting in the class body as a function. This code shouldn't compile at all.

connectedCallback() {
  getDatas({
   isRecentlyView: this.isRecentlyView,
   fields: fields,
   sobjectType: this.sObjectType,
   query: query,
   limitNum: this.numberOfRecords})
   .then(result => {
        console.log(result);
        this.parentdata = result;})
   .catch(e => {
        console.error(e);})
  }
}
2
  • Hi @sfdcfox, I'm sorry for not posting enough. I've just refined my code and add result from debug console. => I call apex method inside a function instead of connectedCallBack => limitnum get passed from config xml file so it will never be null
    – Pam
    Aug 16, 2023 at 2:22
  • Thank you for your answer and refinements. I found out the problem is because of the way data is passed in my code. It will be lost after a user clicks save. @api (public) attribute works fine. No problem about it.
    – Pam
    Aug 16, 2023 at 14:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .