1

EDIT: I originally left out a part of my code which I thought was irrelevant to the question but on further investigation realized it was the source of the issue. Adding it in and then will also add the answer.

I was actually calling handleReleaseHold() instead of directly calling doReleaseHoldRequest() as I mentioned


I am trying to display a toast notification in Lightning Experience with the results of an API callout from my JS file. I'm not getting an error, but no toast is displaying. Makes me think that it’s an issue of event bubbling/listening from within the JS fetch method, but I haven’t been able to figure it out.

JS file:

import { LightningElement, track, api, wire } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class HoldModal extends LightningElement {  
    @track accountId = '001P000001OUxqZIAT';
    @track msgToDisplay = 'default';
    
/// Added in this section - start
    handleReleaseHold(){
        this.doReleaseHoldRequest();
        this.doCloseModal();
    }

    doCloseModal() {
        const buttonPressed = "save";
        const closeModalEvent = new CustomEvent('modalClosed', { detail: buttonPressed });
        this.dispatchEvent(closeModalEvent);
    }
/// Added in this section - end

    doDisplayToastMsg(variant, title, message) {
        const toastEvt = new ShowToastEvent({
            variant: variant,
            title: title,
            message: message,
        });
        this.dispatchEvent(toastEvt);
    }

    doReleaseHoldRequest(){
        const url = 'https://www.url.com';
        const params = {
            method: 'POST',
            headers: {
              "Content-Type" : "application/json",
              "Authorization" : "Token token=xxxxxxxxxxxxxxxxxxx"
            },
            body: '{"key": "value"}'
          };
        const thisReference = this;  // different this scope when called from within subfunctions, so setting variable here to use correct this
        fetch(url, params)
        .then((response) => response.json())
        .then(function(data){
            if(data.message){
               // handle later
            }
            else if(data.errors){
                // 1. // console.log('thisReference.accountId: ' + thisReference.accountId);
                // 3. // thisReference.doDisplayToastMsg('error', 'within fetch', 'this is the error');
                console.log('thisReference.msgToDisplay - before: ' + thisReference.msgToDisplay);
                // 4. // thisReference.msgToDisplay = 'MESSAGE INSIDE FETCH';
                console.log('thisReference.msgToDisplay - after: ' + thisReference.msgToDisplay);
                // 4.a. // thisReference.doDisplayToastMsg('error','within fetch',thisReference.msgToDisplay);
                /* 5. const toastEvt = new ShowToastEvent({
                        variant: 'error',
                        title: 'title',
                        message: 'message',
                    });
                 thisReference.dispatchEvent(toastEvt);
                */
                
            }
        })
        .then(function(){
            // 6. //thisReference.doDisplayToastMsg('error','last then before catch' ,thisReference.msgToDisplay);
        })
        .catch(function(error){
            console.log('doReleaseHoldRequest error: ' + error);
        })
        // 2. // thisReference.doDisplayToastMsg('error','after fetch', 'this is the error');
        // 4.b. // thisReference.doDisplayToastMsg('error','after fetch',this.msgToDisplay);
    }
}

When I call doReleaseHoldRequest() I do get into the else if(data.errors){ block fine which is where I'm trying to call the toast

Things I’ve tried (numbers associated with commented out sections in above code):

  1. Confirmed that thisReference.accountId prints what I expect so seems that correct this context is being used

  2. Confirmed that firing a toast message with my doDisplayToastMsg() helper method AFTER the fetch() method works => the toast displays correctly as expected

  3. Tried firing toast message with doDisplayToastMsg() helper method WITHIN the fetch method => no toast is displayed
  4. Tried populating @track msgToDisplay with the results from the fetch method and then calling the doDisplayToastMsg()

    a. right afterwards within the fetch() method => no toast is displayed

    b. after the fetch method => toast is displayed but not with updated message content (which makes sense, because the toast is probably being displayed before the fetch method is complete)

  5. Tried creating and dispatching a toast event without using my helper method doDisplayToastMsg() (which really shouldn't make a difference, but just in case) => no difference, toast still did not display

  6. Added another .then() and called the doDisplayToastMsg() there with a reference to the message content variable => no toast is displayed
2

added

You get error message ONLY when this is undefined - not when this is re-scoped to different object. For your toast to work correctly this should refer to class HoldModal as toast should be invoked on instance of the class and not on any other element.


Although you are trying to save the scope of this by using const thisReference = this;, the scope of this has already changed because it has been invoked from catch block. The simplest way to fix the issue is using arrow function - does not have own scope so the scope of this is always intact.

fetch(url, params)
    .then((response) => response.json())
    .then((data) => {
    if (data.message) {
        // handle later
    }
    else if (data.errors) {
        this.doDisplayToastMsg('error','within fetch',this.msgToDisplay);
    }
})
.then(() => {
    // logic
})
.catch((error) => {
    console.log('doReleaseHoldRequest error: ' + error);
})
| improve this answer | |
  • That is helpful to know that the arrow function allows me to keep the this scope and that part works for me. However it doesn't seem like an issue with the scope because the console.log('thisReference.accountId: ' + thisReference.accountId); prints correctly. And when I switched it using the arrow function and then calling this directly, the toast still doesn't display – dmorse Sep 24 '19 at 11:44
  • When I don't have the this scope correct, I get the following error message: TypeError: Cannot read property 'doDisplayToastMsg' of undefined But when the scope is correct I don't get the error message but the toast still does not display – dmorse Sep 24 '19 at 11:45
  • added in answer. Did you try arrow function? by removing all functions? it should definitely work with arrow function as there will be no confusion – salesforce-sas Sep 24 '19 at 12:40
  • (facepalm) It is working now. Turns out I had another piece of the code that was closing the modal before the fetch completed so by the time it got to the doDisplayToastMsg() the HoldModal component had been removed. It was working for the doDisplayToastMsg() called after the fetch function because that was getting called before the modal was being closed. Thanks for the pointer about the arrow function though - that part is cleaner! – dmorse Sep 24 '19 at 14:02
  • WC. +1 good question though. – salesforce-sas Sep 24 '19 at 14:05
1

Turns out that the doCloseModal() function was closing the modal before the doDisplayToastMsg() function inside the fetch() was getting a chance to run. The doDisplayToastMsg() after the fetch() was displaying the toast correctly because it ran before the fetch() completed.

Solution was to move the doCloseModal() to inside the fetch(), after the doDisplayToastMsg() is called. Then the modal closes and also the toast displays as desired

| improve this answer | |
0

You have to many then().then() promises, this scope has changed a lot.

Then when you call this.dispatchEvent(toastEvt); , this does not point to the component. A quick thing I can think of is to provide component reference in doDisplayToastMsg

doDisplayToastMsg(variant, title, message , componentThis) {
        const toastEvt = new ShowToastEvent({
            variant: variant,
            title: title,
            message: message,
        });
        componentThis.dispatchEvent(toastEvt);
    }

And then call it as

thisReference.doDisplayToastMsg('error','within fetch',thisReference.msgToDisplay,thisReference);

Another way would be to bind this to the promise or to dispatch event.

| improve this answer | |
  • It doesn't seem to be an issue with the this scope because I do get the reference to the console.log('thisReference.accountId: ' + thisReference.accountId); properly printing. I tried passing the scope into my doDisplayToastMsg but it didn't seem to help – dmorse Sep 24 '19 at 11:41

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.