2

TL;DR whats the best way to tell a page that the underlying data has loaded through a mix of imperative calls and wire calls, preferably without chaining .then functions

Long Version:

I have 4 async requests that I want to perform before showing the page.

My first initial try was to have a <template if:true={loaded}> tag to show the form when it's loaded and a <template if:false={loaded}> tag to show a spinner while it's loaded. The loaded property is actually a getter. However when I updated the three properties that hold the async data my getter doesn't get recalled

Html:

<template>
    <template if:false={loaded}>
        <lightning-layout>
            <lightning-layout-item size="12" class="slds-text-align_center">
                <div style="min-height: 100px;">
                    <lightning-spinner alternative-text="asset loading" size="large"></lightning-spinner>
                </div>
            </lightning-layout-item>
        </lightning-layout>
    </template>
    <template if:true={loaded}>
        <lightning-layout multiple-rows="true" padding="around-small">
            <!--content-->
        </lightning-layout>
    </template>
</template>

Controller:

import { getObjectInfo } from 'lightning/uiObjectInfoApi';
import { getPicklistValues } from 'lightning/uiObjectInfoApi';
import ASSET_OBJECT from '@salesforce/schema/Asset';
import CURRENCY_ISO_FIELD from '@salesforce/schema/Asset.CurrencyIsoCode';
import CHANNEL_MODE_FIELD from '@salesforce/schema/Asset.Channel_Mode__c';
@track
model;
@track
currencyPicklistValues;
@track
channelModePicklistValues;
@track
loaded;

@wire(getObjectInfo, {objectApiName: ASSET_OBJECT})
objectInfo

@wire(getPicklistValues, {recordTypeId: '$objectInfo.data.defaultRecordTypeId', fieldApiName : CURRENCY_ISO_FIELD})
wiredCurrencyPickLists({data}) {
    if (data) {
        this.currencyPicklistValues = data;
    }
}

@wire(getPicklistValues, {recordTypeId: '$objectInfo.data.defaultRecordTypeId', fieldApiName : CHANNEL_MODE_FIELD})
wiredChannelPicklists({data}) {
    if (data) {
        this.channelModePicklistValues = data;
    }
}

connectedCallback() {
    getAssetModel({assetId: this.recordId})
    .then(result => {
        this.originalModel = JSON.parse(JSON.stringify(result));
        this.model = result;
        this.selectedRevision = result.revisions.find((revision) => {
            return revision.majorRevision.Id === result.asset.Current_Major_Revision__c;
        });
    })
    .catch(err => {
        this.error = err;
    });
}

get loaded() {
    return this.model && this.currencyPickListValues && this.channelModePicklistValues;
}

Since get loaded doesn't get called after the underlying properties change I attempted to use async/await patterns and just set loaded as a @track property. However now I'm running into issues with wrapping @wire properties in a promise.

Parsing error: Leading decorators must be attached to a class declaration (62:12)

I also can't call the wire methods imparatively otherwise I get an error

Imperative use is not supported. Use @wire(getObjectInfo).

connectedCallback() {
    (async(that) => {
        debugger;
        const objectInfo = that.asyncWireRequest(getObjectInfo, {objectApiName: ASSET_OBJECT});
        that.channelModePicklistValues = await that.asyncWireRequest(getPicklistValues, {recordTypeId: objectInfo.data.defaultRecordTypeId, fieldApiName : CHANNEL_MODE_FIELD});
        that.currencyPicklistValues = await that.asyncWireRequest(getPicklistValues, {recordTypeId: objectInfo.data.defaultRecordTypeId, fieldApiName : CURRENCY_ISO_FIELD})
        that.model = await that.asyncRequest(getAssetModel, {assetId : this.recordId});
        that.originalModel = JSON.parse(JSON.stringify(that.model));
        that.selectedRevision = that.revisions.find((revision) => {
            return revision.majorRevision.Id === that.model.asset.Current_Major_Revision__c;
        });
        that.loaded = true;
    })(this);
}

asyncImperativeRequest = (apexMethod, parameters) => {
    return new Promise((accept, reject) => {
        apexMethod(parameters)
        .then(result => {
            accept(result);
        })
        .catch(error => {
            reject(error);
        })
    });
}

asyncWireRequest = (wireMethod, parameters) => {
    return new Promise((accept, reject) => {
        @wire(wireMethod, parameters)
        wireHandler({error, data}) {
            if (data) {
                accept(data);
            } else {
                reject(error);
            }
        }
    });
}
2
  • I know its not a good idea can't we put all in connected callback Aug 23, 2019 at 3:09
  • @AvijitChakraborty, as he mentioned in question, he cant invoke getPicklistValues imperatively and wire-services are like self-invoked standard functions (type of) which cannot be put in other methods. Aug 23, 2019 at 3:19

1 Answer 1

6
  1. Getters will be invoked only when the properties on which they are dependent on are reactive. You need to make below 3 properties reactive for rerendering loaded:

    @track model;
    @track currencyPicklistValues;
    @track channelModePicklistValues;  
    
  2. When you want to check if data exists in a variable, it is a good practice to use !!.

    get loaded() {
        return !!this.model && !!this.currencyPickListValues && !!this.channelModePicklistValues;
    }
    

ADDED

Check this playground link for further understanding.

9
  • Went back and actually retried this and now it seems to work. I either had a variable typo, or it was cached on a previous attempt Aug 23, 2019 at 13:29
  • Happened with me some times, we`ll never know if its typo or what weird mistake we did after trying for hours together.... suddenly works with same approach next day morning :) Aug 23, 2019 at 13:30
  • this is strange. It works about 50% of the time... Aug 23, 2019 at 14:38
  • can you create a playground? Aug 23, 2019 at 14:39
  • I think it's the @wire callbacks that can't be reproduced in the playground. I think they're just slow. This would explain why I thought it wasn't working the first time Aug 23, 2019 at 14:45

You must log in to answer this question.

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