Salesforce LWC學習(三十九) lwc下quick action的recordId的問題和解決方案

zero.zhang發表於2022-01-04

本篇參考:

https://developer.salesforce.com/docs/component-library/bundle/force:hasRecordId/documentation

https://developer.salesforce.com/docs/component-library/documentation/en/lwc/use_record_context

Salesforce LWC學習(三十六) Quick Action 支援選擇 LWC了

我們在使用lwc的時候,recordId的嵌入以及wire adapter的功能,極大的減輕了我們的開發壓力,讓我們很爽的使用著。後來隨著release的不斷增強,lwc也支援quick action。這個我們在之前的篇章也講過。曾經對recordId的使用不是很深入,隨著quick action的一個功能的使用,發現了recordId在lwc下的一個隱藏描述(或者直接說是bug也好)。我們先來一個大家常用並且看上去沒有問題的程式碼

testLwcQuickAction.html

<template>
    <lightning-quick-action-panel header="Test LWC Quick Action">
        {name}
    </lightning-quick-action-panel>
</template>

testLwcQuickAction.js

import { LightningElement, track, wire,api } from 'lwc';
import { CloseActionScreenEvent } from 'lightning/actions';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import NAME_FIELD from '@salesforce/schema/Account.Name';
export default class testLwcQuickAction extends LightningElement {
    @api recordId;

    @wire(getRecord, { recordId: '$recordId', fields: [NAME_FIELD]})
    account;

    get name() {
        return getFieldValue(this.account.data, NAME_FIELD);
    }
}

將這個lwc配置成一個quick action,型別選擇lightning web component,找到一條account,我們看一下效果。

展示正常,沒啥問題。那我們有時候會使用quick action做callout或者後臺互動,當然可以使用headless的quick action,但是為了UI美觀,我們可以使用screen的quick action,執行時展示 spinner,執行結束消失,讓使用者不會以為頁面假死。我們進行下個程式碼展示。

TestLwcQuickActionController.cls

public with sharing class TestLwcQuickActionController {
     
     @AuraEnabled(cacheable=false)
     public static Boolean updateAccount(String accountId) {
         Account accountItem = new Account();
         accountItem.Id = accountId;
         accountItem.Name = 'updated account : ' + String.valueOf(System.now());
         update accountItem;
         return true;    
     }   
}

testLwcQuickAction2.html

<template>
<lightning-quick-action-panel header="Test LWC Quick Action">
</lightning-quick-action-panel>
</template>

testLwcQuickAction2.js

import { LightningElement, track, wire,api } from 'lwc';
import { CloseActionScreenEvent } from 'lightning/actions';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import updateAccount from '@salesforce/apex/TestLwcQuickActionController.updateAccount';
export default class testLwcQuickAction2 extends LightningElement {
    @api recordId;

    renderedCallback() {
        updateAccount({accountId : this.recordId})
        .then(result => {
            console.log(result);
            let event = new ShowToastEvent({
                    title: 'update success',
                    variant: 'success'
                });
                this.dispatchEvent(event);
                this.dispatchEvent(new CloseActionScreenEvent());
        })
        .catch(error => {
            console.log(JSON.stringify(error));
            let event = new ShowToastEvent({
                    title: 'error occurs',
                    variant: "error"
                });
            this.dispatchEvent(event);
            this.dispatchEvent(new CloseActionScreenEvent());
        });
    }
}

乍一眼看上去是不是一點問題都沒有,讓我們實際執行一下

竟然報錯了,提示沒有recordId,我們將這個不作為 lwc 的quick action,複製這個程式碼,放在 lightning record page發現程式碼正常執行,只有作為quick action情況下,recordId為null??? 當然,不止renderedCallback, connectedCallback下,recordId同樣為空。我們找到文件,提示只有顯示UI的上下文才可以正常的使用 recordId,其他的情況下則不支援。當然,報錯原因是 recordId我們沒有判斷非空,這個主要是為了暴露問題,如果使用非空驗證,仍然不會執行後臺。

那麼問題來了,什麼是 explicit record context?我們哪裡可以查到呢? 至少lwc的文件中沒有檢視到,所以我們需要先找到 aura的文件,因為aura是lightning experience的第一版,我們只需要看一下 force:hasRecordId的文件去碰一下運氣看看有沒有即可。很幸運地是,我們找到了文件,並且瞭解了什麼算是顯示記錄的上下文。

 

通過描述愈發的感覺這是因為 lwc quick action的相容性導致的問題,或者說是一個bug,因為這個並不符合說的顯示記錄的上下文的描述,而且同樣程式碼作為元件放在record page即可以生效。當然問題既然發現,找到workaround方案就可以了。解決這個問題,目前想到3種 workaround方案,每個方案都親測可以解決問題。

1. 前端展示 recordId,放在 div 設定 style="display:none"即可,這樣就滿足了顯示記錄上下文的要求,將js內容前端展示,則會強制嵌入。

testLwcQuickAction2.html

<template>
<lightning-quick-action-panel header="Test LWC Quick Action">
    <div style="display: none;">
        {recordId}
    </div>
</lightning-quick-action-panel>

</template>

testLwcQuickAction2.js:renderedCallback先判斷recordId非空

import { LightningElement, track, wire,api } from 'lwc';
import { CloseActionScreenEvent } from 'lightning/actions';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import updateAccount from '@salesforce/apex/TestLwcQuickActionController.updateAccount';
export default class testLwcQuickAction2 extends LightningElement {
    @api recordId;

    renderedCallback() {
        if(this.recordId) {
            updateAccount({accountId : this.recordId})
            .then(result => {
                console.log(result);
                let event = new ShowToastEvent({
                        title: 'update success',
                        variant: 'success'
                    });
                    this.dispatchEvent(event);
                    this.dispatchEvent(new CloseActionScreenEvent());
            })
            .catch(error => {
                console.log(JSON.stringify(error));
                let event = new ShowToastEvent({
                        title: 'error occurs',
                        variant: "error"
                    });
                this.dispatchEvent(event);
                this.dispatchEvent(new CloseActionScreenEvent());
            });
        }
        
    }
}

2. aura型別quick action,aura搭配lwc的組合YYDS

testQuickActionForAura.cmp: aura下嵌入 recordId正常

<aura:component implements="force:hasRecordId,force:lightningQuickActionWithoutHeader">
    <aura:attribute name="recordId" type="String"></aura:attribute>
    <c:testSonLwcQuickAction recordId="{!v.recordId}" onclosemodal="{!c.handleCloseAction}"></c:testSonLwcQuickAction>
</aura:component>

testQuickActionForAuraController.js

({
    handleCloseAction : function(component, event, helper) {
        $A.get('e.force:refreshView').fire();
        $A.get("e.force:closeQuickAction").fire();
    }
})

testSonLwcQuickAction.js

import { LightningElement, track, wire,api } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import updateAccount from '@salesforce/apex/TestLwcQuickActionController.updateAccount';
export default class testSonLwcQuickAction extends LightningElement {
    @api recordId;

    renderedCallback() {
        updateAccount({accountId : this.recordId})
        .then(result => {
            console.log(result);
            let event = new ShowToastEvent({
                    title: 'update success',
                    variant: 'success'
                });
                this.dispatchEvent(event);
                this.dispatchEvent(new CustomEvent('closemodal'));
        })
        .catch(error => {
            console.log(JSON.stringify(error));
            let event = new ShowToastEvent({
                    title: 'error occurs',
                    variant: "error"
                });
            this.dispatchEvent(event);
            this.dispatchEvent(new CustomEvent('closemodal'));
        });
    }
}

lwc的js呼叫後臺,執行以後,event dispatch,aura關閉quick action modal,此種方式親測有效。

3. 使用CurrentPageReference獲取recordId,獲取以後,再去執行後臺方法

testLwcQuickAction.html

<template>
<lightning-quick-action-panel header="Test LWC Quick Action">
</lightning-quick-action-panel>
</template>

testLwcQuickAction.js

import { LightningElement, track, wire,api } from 'lwc';
import { CloseActionScreenEvent } from 'lightning/actions';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { CurrentPageReference } from 'lightning/navigation';
import updateAccount from '@salesforce/apex/TestLwcQuickActionController.updateAccount';
export default class testLwcQuickAction3 extends LightningElement {
    @track recordId;
    @wire(CurrentPageReference)
    pageRef;

    renderedCallback() {
        if(this.pageRef && this.pageRef.state) {
            this.recordId = this.pageRef.state.recordId;
            updateAccount({accountId : this.recordId})
            .then(result => {
                console.log('*** result : ' + result);
                let event = new ShowToastEvent({
                        title: 'update success',
                        variant: 'success'
                    });
                    this.dispatchEvent(event);
                    this.dispatchEvent(new CloseActionScreenEvent());
            })
            .catch(error => {
                console.log(JSON.stringify(error));
                let event = new ShowToastEvent({
                        title: 'error occurs',
                        variant: "error"
                    });
                this.dispatchEvent(event);
                this.dispatchEvent(new CloseActionScreenEvent());
            });
        }

    }
}

簡單演示:點選按鈕以後,可以正常的獲取 recordId並且可以正常的執行

總結: 篇中只是暴露出recordId在lwc quick action下的問題,其他的情況暫時使用正常,以及3種workaround方案。篇中demo中沒有考慮快取,也沒有優化程式碼,感興趣的小夥伴自行優化。有更好的方法歡迎交流溝通。篇中錯誤地方歡迎指出,有不懂歡迎留言。

相關文章