4

In my component, I have an api setter method for recordId and I am generating a URL based on the given recordId inside the setter. I am storing the generated URL and using it as href value like this <a href={recordPageUrl}>...</a>.

The component is inherited from

... extends NavigationMixin(LightningElement) {
    @api
    set recordId(value) {
        this._recordId = value;
        
        this[NavigationMixin.GenerateUrl]({
            type: 'standard__recordPage',
            attributes: {
                recordId: '0050R00000AVZsgQAH',
                actionName: 'view'
            }
        }).then((url) => {
            this.recordPageUrl = url;
            console.log(url)
        });
    }

The promise is returning JavaScript:void(0) instead of URL. I tried to remove the setter and move this[NavigationMixin.GenerateUrl]({...}) to connectedCallback then it is working fine and the promise is returning a valid URL.

I have looked into the documentation of PageReference Types and I am not missing any required attribute. On the other hand, salesforce documentation is always using the GenerateUrl inside the connectedCallback.

Summary: Any idea why NavigationMixin.GenerateUrl is not working inside the setter?

2
  • Are you using the url in an <a> tag or do you need to open the page with a event handler? Commented Dec 30, 2021 at 2:48
  • yes I am using the url in the href and I don't have any handler attached to the <a> Commented Dec 30, 2021 at 14:14

3 Answers 3

0

There are a few ways that you can do this. One way is to still use the connectedCallback, but make it async and do the following. Also, remove the setter for recordId. This is a bit cleaner.

async connectedCallback() {
    this.recordPageUrl = await this.getUrl();
}

getUrl() {
    return this[NavigationMixin.GenerateUrl]({
        type: 'standard__recordPage',
        attributes: {
            recordId: this.recordId,
            actionName: 'view',
        },
    });
}

You can also use a getter for the recordPageUrl as well and remove the setter again for the recordId.

_recordPageUrl;

get recordPageUrl() {
    this.getUrl().then((url) => {
        this._recordPageUrl = url;
    });
    return this._recordPageUrl;
}

getUrl() {
    return this[NavigationMixin.GenerateUrl]({
        type: 'standard__recordPage',
        attributes: {
            recordId: this.recordId,
            actionName: "view"
        }
    });
}
4
  • In your example, you already have the recordId available but in my case, my component is executing some async code to get a recordId and then I am calling the setter of the child component and passing the recordId. Commented Dec 30, 2021 at 14:28
  • The record Id would be a public api property in my example. If you need to call a method on your child component create a public function. The public function can also be an async function. Commented Dec 30, 2021 at 14:35
  • yes, I can make it work like that but back to the question, why it's not working inside the setter? if the setter is working then I don't need to add any additional method to the component except setter. Commented Dec 30, 2021 at 14:44
  • What's the issue then? I tested both methods above using a parent component passing the record Id into child and the url will be set in each scenario. Why would we need to set the url in a recordId setter? That's not the general use case for a setter. The issue is that navigation mixin returns a promise, so you either need a getter or use the connected callback. Commented Dec 30, 2021 at 15:00
0

Annoyingly, wrapping your call to the NavigationMixin in a setTimeout resolves this issue. I have to imagine this is the result of some amount of spaghetti in the LWC engine.

... extends NavigationMixin(LightningElement) {
    @api
    set recordId(value) {
        this._recordId = value;
        
        window.setTimeout(() => this[NavigationMixin.GenerateUrl]({
            type: 'standard__recordPage',
            attributes: {
                recordId: '0050R00000AVZsgQAH',
                actionName: 'view'
            }
        }).then((url) => {
            this.recordPageUrl = url;
            console.log(url)
        }), 0);
    }
1
  • Thanks for the idea. To me setTimeOut(..) seems like a workaround and we might have to save the timeoutID() and then add another line to call the clearTimeOut() afterward. Commented Jan 12, 2023 at 11:01
0

So wanted to share my experience here as I experienced a similar issue to what @Raja was experiencing.

When I was doing my initial build out of a component I was working on, I was calling the NavigationMixin.GenerateUrl in an async connectedCallback() method which was working perfectly.

However I noticed that my component wasn't updating when new data was being passed to it. So I then did what Raja mentioned in his original post. I moved my original logic from the async connectedCallback() and into its own async function and then called it from a setter:

async generateTaskUrls() {
        if (this._tasks && this._tasks.length > 0) {
            let tempTasks = JSON.parse(JSON.stringify(this._tasks));
            this._tasks = await Promise.all(
                tempTasks?.map(async (task) => {
                    let tempUrl = await this[NavigationMixin.GenerateUrl]({
                        type: "standard__recordPage",
                        attributes: {
                            recordId: task.Id,
                            objectApiName: "Task",
                            actionName: "view"
                        }
                    });

                    let tempTask = JSON.parse(JSON.stringify(task));

                    tempTask["recUrl"] = tempUrl;
                    return tempTask;
                })
            );
        }
    }

When I did this, I started getting javascript:void(0) for my URLs. I was baffled because I was doing the same exact logic but not in the async connectedCallback().

So then what I did was I started to set a boolean variable in the async connectedCallback() to then determine when the component is fully loaded:

async connectedCallback() {
    await this.generateTaskUrls();
    this.navMixInLoaded = true;
}

get tasks() {
    return this._tasks;
}
set tasks(value) {
    this._tasks = value;
    if(this.navMixInLoaded) {
        this.generateTaskUrls();
    }
}

This seemed to do the trick. Just in case anyone else experiences this issue.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.