15

I'm not able to get refreshApex() working in an lwc. I've read the docs here -- scroll to the bottom. Here are the key elements of the js file:

import { LightningElement, track, api, wire } from 'lwc';
import getBlasts from '@salesforce/apex/Blast.getBlasts';
import { refreshApex } from '@salesforce/apex';

export default class BlastWaveTree extends LightningElement {
    @api recordId;
    @track blastData;
    @track error;
    @wire(getBlasts, { CampaignId: '$recordId' })
        wireBlastData({
            error,
            data
        }) {
            if (data) {
                let tempjson = JSON.parse(JSON.stringify(data).split('waves').join('_children'));
                this.blastData = tempjson;

        } else {
                this.error = error;
        }
    }

    @api
    refresh() {
        return refreshApex(this.blastData); 
    }

My code varies from the doc examples for 2 reasons:

  1. The example code doesn't separate out the Apex response into error and data, the pattern I've been following as shown elsewhere.
  2. This data is going into a lightning-tree-grid so I have to process it a bit before assigning it to blastData.

I've tried return refreshApex(this.wireBlastData) as well, and that doesn't work.

Ultimately, I want to call the refresh function from the parent component (hence, the @api), but I've simplified it (till I get it working) by calling the refresh function from a button on this same component. I added a console.log line in the refresh() function so I know the button is calling the function. It just doesn't refresh. I'm also watching the apex logs as I push the refresh button, and there's no apex activity.

Here's the template:

<template>
<lightning-card title="Blasts" icon-name="custom:custom3">
    <div slot="actions">
        <lightning-button-group >
            <lightning-button-icon icon-name="utility:refresh"  alternative-text="Refresh" onclick={refresh}></lightning-button-icon>
            <lightning-button label={buttonLabel} onclick={toggleWaves}></lightning-button> 
            <lightning-button label="New Blast" onclick={onNewBlast} variant="Brand"> </lightning-button>
        </lightning-button-group>
    </div>
    <div class="slds-m-around_medium">
        <lightning-tree-grid 
            data={blastData} 
            columns={columns} 
            key-field="id" 
            hide-checkbox-column="true" 
            onrowaction={handleRowAction} >
        </lightning-tree-grid>
    </div>
</lightning-card>

What am I missing?

Here's the error detail from the browser dev tools:

    lds.js:2 Uncaught (in promise) Error: Refresh failed because resolved configuration is not available.
    at Object.e.refresh [as refreshApex] (lds.js:2)
    at l.refresh (blastWaveTree.js:4)
    at callHook (aura_prod.js:24)
    at sa (aura_prod.js:2)
    at aura_prod.js:2
    at _ (aura_prod.js:2)
    at HTMLElement.e (aura_prod.js:2)
    at l (aura_prod.js:2)
    at Un.call.a (aura_prod.js:2)
    at Array.forEach (<anonymous>)
e.refresh   @   lds.js:2
refresh @   blastWaveTree.js:4
callHook    @   aura_prod.js:24
sa  @   aura_prod.js:2
(anonymous) @   aura_prod.js:2
_   @   aura_prod.js:2
e   @   aura_prod.js:2
l   @   aura_prod.js:2
Un.call.a   @   aura_prod.js:2
t   @   aura_prod.js:2
Lt  @   aura_prod.js:2
t.$$lwcEventWrapper$$   @   aura_prod.js:2
  • Do you mind sharing your template code as well, as how you are invoking the refresh function? – Jayant Das Feb 26 '19 at 18:35
  • Note, I didn't include everything in the js file, just the parts about getting the data and refreshing. The other stuff is for handling row action, toggling the child rows of data, etc. – PatMcClellan__c Feb 26 '19 at 18:54
16

I am still trying to find out more on this, but the only way I found refreshApex to be working by going through one of the examples in lwc-recipe was by refreshing it imperatively as below.

wiredResults;

@wire(getBlasts)
imperativeWiring(result) {
    this.wiredResults = result;
    if(result.data) {
        this.blastData = JSON.stringify(result.data);
    }
}


@api
refresh() {
    return refreshApex(this.wiredResults); 
}

Few observations during my test:

  • It seems you cannot utilize the wireBlastData function here and that you will need to introduce another function (in my example imperativeWiring) to call the Apex method imperatively
  • If the same variable blastData was utilized in the new function, it resulted in an error (you will be able to view this in console)
  • Had to introduce a new property (wiredResults in this example) and use that in the imperativeWiring function
  • Had to make sure that the initialization was as this.wiredResults = result. Doing something as this.wiredResults = result.data threw an error (this seems to be the reason why it cannot be utilized with wireBlastData function)
  • The Apex method was successfully called with this approach, but putting any alert or console.log in imperativeWiring function did not seem to have an effect (I am still not able to figure this out, why!)
  • 2
    Thanks for your effort. At least it's not just me! – PatMcClellan__c Feb 26 '19 at 19:54
  • 2
    It just seems that its not very well documented, but it does perform what is expected out of it. – Jayant Das Feb 26 '19 at 19:56
  • 1
    Just implemented and it works! Thanks for your help. – PatMcClellan__c Feb 26 '19 at 20:02
  • @JayantDas have you manged to get refreshApex work without any change in cache/database? – Pranay Jaiswal Apr 8 '19 at 15:10
  • @PranayJaiswal Can you clarify a bit, it seemed to work with what I have here when I tried it last time. E.g., when there was a change in the database, with an imperative refresh it would show the data. – Jayant Das Apr 8 '19 at 15:22
8

Thank you for raising this. I've filed a documentation bug to update Call Apex Methods's Refresh the Cache section with greater clarity.

refreshApex() operates on the object provisioned by @wire. That's why code like this works:

import getBlasts from '@salesforce/apex/Blast.getBlasts';
import { refreshApex } from '@salesforce/apex';
export default class BlastWaveTree extends LightningElement {
    @wire(getBlasts, { ... })
    provisionedValue;

    refresh() {
        return refreshApex(this.provisionedValue); 
    }

When you @wire to a function it is invoked with the provisioned value. When you use object destructuring you're extracting values from the provisioned value, losing the provisioned value itself. Instead you want to do something like this:

import getBlasts from '@salesforce/apex/Blast.getBlasts';
import { refreshApex } from '@salesforce/apex';
export default class BlastWaveTree extends LightningElement {
    provisionedValue; 

    @wire(getBlasts, { ... })
    wireBlastData(provisionedValue) {
        this.provisionedValue = provisionedValue; // track the provisioned value
        const { data, error } = provisionedValue; // destructure it for convenience
        if (data) { ... }
        else if (error) { ... }
    }

    refresh() {
        return refreshApex(this.provisionedValue); 
    }

I found your use of JSON.parse/stringify unexpected. Why are you transforming like that instead of returning the desired shape from your Apex class?

  • That's code I found on a blog, can't remember where, that explained how to do a tree grid. Not sure why it's cloning the data. – PatMcClellan__c Jun 30 '19 at 2:13
  • It's cloning the data because the data provisioned by the wire service is read-only, so the JSON string can't be modified to format correctly for the tree grid. The blog likely transformed the data in the lwc because that allows the Apex class to directly return the result of an sObject query/subquery, and only requires a single line of code in the lwc JS to get the formatting correct. It would be more code to transform in the Apex class. – womanwhodevs Nov 27 '19 at 22:55
  • Hi Kevin - Why is does refreshApex need to return the method? – Robs Apr 8 '20 at 10:19
  • I was matching the original question's semantics for refresh. refreshApex() returns a promise that resolves after the refresh is completed. If that event is of interest you can wait for the promise to resolve/reject. – Kevin Venkiteswaran Apr 14 '20 at 5:01

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.