11

I made a lwc to use as a quick action, but despite declaring an @api recordId property, when logging it in connectedCallback, I get undefined. This is the configuration:

<targets>
    <target>lightning__RecordAction</target>
</targets>
 <targetConfigs>
<targetConfig targets="lightning__RecordAction">
  <actionType>ScreenAction</actionType>
</targetConfig>

Is the only way to pass the recordId, wrapping my lwc in an aura component? I thought, as lwc quick actions are only supported in record pages, that the recordId would be passed correctly.

4 Answers 4

17

I had to test this, but it seems there's two different behaviors for the different actionTypes.

  1. ScreenAction

recordId is undefined in connectedCallback and renderedCallback unless you use it in your html. In that situation, only renderedCallback has the value of recordId.

Even something as simple as a conditional with recordId makes it accessible in renderedCallback.

<template>
    <template if:true={recordId}>
        test
    </template>
</template>
import { LightningElement, api } from 'lwc';

export default class Testing extends LightningElement {
    @api recordId;

    connectedCallback() {
        console.log('connected===============');
        console.log(this.recordId + ' is null');
    }

    renderedCallback() {
        console.log('rendered------------');
        console.log(this.recordId + ' is provided');
    }
}

If the variable is not in the html template, it's not provided to you without extra changes on your part - you can look at the other answer for using CurrentPageReference or use getRecord as shown in lwc-recipes and Create Screen Quick Actions

@wire(getRecord, { recordId: '$recordId', fields: FIELDS })

  1. Action

This is a headless action from the quick action (no screen). This has the value provided to it in an invoke call as noted in documentation since there's no rendering going on.

import { LightningElement, api } from "lwc";
 
declare default class HeadlessSimple extends LightningElement {
  @api recordId; 

  @api invoke() {
    console.log(this.recordId);
  }
}
3
  • 2
    Yes, this is exactly it. when I rendered the record Id in my html, it showed, so it is being passed correctly, just a life cycle thing. Thanks for the details! Jun 16, 2021 at 14:09
  • 1
    @Kris Goncalves thanks for showing this. I wonder how come the official documentation does not show this way developer.salesforce.com/docs/component-library/documentation/… Sep 30, 2022 at 15:24
  • Interesting. In a quick test with the setter, that only works if you add @api recordId; so it seems, as an example, incomplete or confusing as to why the setter would be needed at all (considering @api recordId only works). I'll see if I can ask the people who wrote that doc. Sep 30, 2022 at 15:43
8

Yes, We also get record id from ScreenType action.

HTML:

<template>
<lightning-quick-action-panel header="Confirmation">
    <p class="slds-align_absolute-center">Are you sure to cancel this record?</p>

    <div slot="footer">
        <lightning-button variant="neutral" label="No" onclick={closeModal}>
        </lightning-button>
        &nbsp;
        <lightning-button variant="brand" onclick={handleSubmit} label="Yes">
        </lightning-button>
    </div>
</lightning-quick-action-panel>

Javascript:

import { LightningElement, api, wire } from 'lwc';
import { CurrentPageReference } from 'lightning/navigation';
import { CloseActionScreenEvent } from 'lightning/actions';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';

export default class CancelMoneyReceipt extends LightningElement {

 recordId;

 @wire(CurrentPageReference)
 getStateParameters(currentPageReference) {
     if (currentPageReference) {
         this.recordId = currentPageReference.state.recordId;
     }
 }

 handleSubmit() {
     console.log('RECORD_ID : ', this.recordId);
 }

 closeModal() {
     this.dispatchEvent(new CloseActionScreenEvent());
 }
}

XML:

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
   <apiVersion>52.0</apiVersion>
   <isExposed>true</isExposed>
   <targets>
      <target>lightning__RecordAction</target>
   </targets>
   <targetConfigs>
       <targetConfig targets="lightning__RecordAction">
           <actionType>ScreenAction</actionType>
       </targetConfig>
   </targetConfigs>

Thank You

4
  • hey! thanks for the answer, did wiring CurrentPageReference your only way to get the id? Jul 29, 2021 at 12:45
  • Yes, only like this when our action type is ScreenAction. Jul 31, 2021 at 12:59
  • thanks for your post and it really helped me!
    – Nick
    Nov 8, 2021 at 18:34
  • Just adding on top of what Nisar has mentioned For recordId it should be : currentPageReference.attributes.recordId For recordName it should be : currentPageReference.state.recordName Jul 11, 2022 at 17:42
0

Based on my testing, I found screenAction LWC recordId has been passed into, just matter of time. connectedCallback() is running too early so usually the recordId is not assigned yet. so in renderedCallback() might get the recordId but not guaranteed.

To resolve the timing issue, just add wait function in connectedCallback()

connectedCallback() {
  console.log("====connectedCallback:recordId=====", this.recordId);
  this.sleep(1000).then(() => {
    console.log("====connectedCallback:recordId2=====", this.recordId);
    //you will get recordId here!!
   });
}

sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

====== updated 8 Aug, 2023 ======== After reading the salesforce official document, the better way to handle this is by using setter. The document is incomplete, so I post the code beblow.

_recordId;

@api set recordId(value) {
    this._recordId = value;

    //call your functions here.
}
get recordId() {
    return this._recordId;
}
1
  • 1
    This is essentially what Kris Goncalves already explained in his answer, which is accepted by the OP. Your solution is not appropriate because using a timeout with an arbitrary amount of time might result in an undefined error (the server took a bit longer to respond, for example, and now you have an undefined variable). Aug 4, 2023 at 2:16
0

This works for me. The official dev page is incomplete and very unfriendly. I can't believe why they just write a partial code that solely doesn't work.

js

 flowName = 'FlowApiName';

 inputVariables = [];
    
    _recordId;
    @api
    get recordId() {
        return this._recordId;
    }
    
    set recordId(value) {
        if (value !== this._recordId) {
            this._recordId = value;
            this.inputVariables = [{
                name: 'recordId',
                type: 'String',
                value: value,
            }];
            console.log('setter recordId', value);
            console.log('setter inputVariables', this.inputVariables);
        }
    }

set recordId by getter and setter in js side. In html side, the lightning-flow with lwc:if={recordId} to make sure the flow will be executed only when the recordId is set (by getter and setter)

html

<lightning-flow 
    lwc:if={recordId} 
    flow-api-name={flowName} 
    flow-input-variables={inputVariables} 
    onstatuschange={handleFlowFinish}>
</lightning-flow>

You must log in to answer this question.

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