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 – Avijit Chakraborty Aug 23 '19 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. – salesforce-sas Aug 23 '19 at 3:19
5
  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 – TemporaryFix Aug 23 '19 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 :) – salesforce-sas Aug 23 '19 at 13:30
  • this is strange. It works about 50% of the time... – TemporaryFix Aug 23 '19 at 14:38
  • can you create a playground? – salesforce-sas Aug 23 '19 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 – TemporaryFix Aug 23 '19 at 14:45

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.