0

I'd like your help in understanding what was causing issues in my LWC code. The problem is that in one version of my code (version A), the properties of the child that it took from the parent is undefined. I've simplified everything to the important parts.

Version A - not working

Parent JS

export default class MyParentComponent extends NavigationMixin(LightningElement) {
    quoteId;
    quoteRecord;
    opportunityId;
    opportunityRecord;
    _isLoading=true;

    async _init() {
        if(this._isEmpty(this.quoteRecord) || 
            this._isEmpty(this.opportunityRecord)) return;
        this._isLoading = false;
    }

    @wire(getRecord, {recordId: '$opportunityId', fields: OPPORTUNITY_FIELDS_TO_LOAD})
    opportunityRecordWire({ error, data }) {
        if(data) {
            this.opportunityRecord = data;
            this._init();
        } else if(error) {
            this._showToast('An error was encountered!', error, 'error');
        }
    }

    @wire(getRecord, {recordId: '$quoteId', fields: QUOTE_FIELDS_TO_LOAD})
    quoteRecordWire({ error, data }) {
        if(data) {
            this.quoteRecord = data;
            this.opportunityId = this.quoteRecord.fields.SBQQ__Opportunity2__c.value;
            this._init();
        } else if(error) {
            this._showToast('An error was encountered!', error, 'error');
        }
    }
}

Parent HTML

<div if:true={_isLoading}>
    <!-- loading spinner here -->
</div>

<div if:false={_isLoading}>
    <c-my-child-component opportunity-record={opportunityRecord}>
</div>

Version B - working

Parent JS

export default class MyParentComponent extends NavigationMixin(LightningElement) {
    quoteId;
    quoteRecord;
    opportunityId;
    opportunityRecord;
    opportunityName;
    _isLoading=true;

    async _init() {
        if(this._isEmpty(this.quoteRecord) || 
            this._isEmpty(this.opportunityRecord)) return;
        this._isLoading = false;
    }

    @wire(getRecord, {recordId: '$opportunityId', fields: OPPORTUNITY_FIELDS_TO_LOAD})
    opportunityRecordWire({ error, data }) {
        if(data) {
            this.opportunityRecord = data;
            this.opportunityName = this.opportunityRecord.fields.Name.value;
            this._init();
        } else if(error) {
            this._showToast('An error was encountered!', error, 'error');
        }
    }

    @wire(getRecord, {recordId: '$quoteId', fields: QUOTE_FIELDS_TO_LOAD})
    quoteRecordWire({ error, data }) {
        if(data) {
            this.quoteRecord = data;
            this.opportunityId = this.quoteRecord.fields.SBQQ__Opportunity2__c.value;
            this._init();
        } else if(error) {
            this._showToast('An error was encountered!', error, 'error');
        }
    }
}

Parent HTML

<div if:true={_isLoading}>
    <!-- loading spinner here -->
</div>

<div if:false={_isLoading}>
    <c-my-child-component opportunity-name={opportunityName}>
</div>

The main difference between the 2 versions is the property opportunityName that can be found in version B. Version A is trying to pass an entire opportunityRecord object to the child component and Version B is trying to pass just the name string of the same Opportunity record. Version B is working fine, the name string reaches the child component without any problems, but Version A just ends up with a value of undefined for the opportunityRecord property.

The purpose of the _init method is to ensure that both wire methods have executed and have populated the data that they need to populate (quoteRecord and opportunityRecord). The explanation I was considering was that maybe the properties I'm passing from parent to child haven't been assigned a value yet when the child component was called but this _init method should have prevented that. It works for when I'm passing a string property so it should also work for when I'm passing an object, unless passing an object is not allowed but I wasn't able to see that mentioned anywhere.

I've already made sure that I decorated the child properties with @api and that I have the necessary imports.

Any help would be appreciated. Thanks!

1 Answer 1

0

I think your suspicion makes sense

What if instead of using this condition

<div if:false={_isLoading}>
  <c-my-child-component opportunity-record={opportunityRecord}>
</div>

You use this

<div lwc:if={opportunityRecord}>
  <c-my-child-component opportunity-record={opportunityRecord}>
</div>

This should make sure that your child component only loads if the opportunityRecord is populated.

PS.

I used "lwc:if" instead of "if:true" as it is the recommended now but both work.

source: https://help.salesforce.com/s/articleView?id=release-notes.rn_lwc_if_else.htm&release=242&type=5

1
  • 1
    if:true is deprecated, meaning it will not be supported in the future. Better to start changing now.
    – sfdcfox
    Commented May 3 at 16:45

You must log in to answer this question.

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