ทำให้แอตทริบิวต์ของ wrapper มีปฏิกิริยาใน LWC

Aug 19 2020

ฉันจะอธิบายอย่างละเอียดเกินความจำเป็นในปัญหาที่ฉันกำลังเผชิญดังนั้นหากฉันมีแนวทางที่ดีกว่าในการนำสิ่งที่ฉันกำลังทำไปใช้มีใครบางคนสามารถแนะนำได้

ฉันกำลังสร้างหน้าชุมชน Lightning เพื่อแสดงกรณีของผู้ใช้ หน้าแบ่งออกเป็น 3 คอลัมน์ คอลัมน์ด้านซ้ายสุดแสดงกรณีทั้งหมดโดยผู้ใช้ (พร้อมด้วยหัวเรื่องสถานะและจำนวนช่อง) คอลัมน์กลางแสดงความคิดเห็นเกี่ยวกับเคสที่เลือกไว้ในคอลัมน์ซ้ายสุดคอลัมน์ขวาสุดจะแสดงรายละเอียดเพิ่มเติมเล็กน้อยของเคสที่เลือกในคอลัมน์ซ้ายสุด

โปรดดูภาพด้านล่าง

ปมปัญหาคือเมื่อมีคนคลิกปุ่ม "ปิดเคส" ในคอลัมน์ขวาสุดระบบจะเรียกเซิร์ฟเวอร์ให้เปลี่ยนสถานะเป็น "ปิด" วิธีนี้ใช้งานได้ดี แต่ฉันต้องอัปเดตสถานะที่แสดงในคอลัมน์ซ้ายสุดและที่ฉันมีปัญหา

นี่คือรายละเอียดของการใช้งาน

แต่ละคอลัมน์เป็นส่วนประกอบและทั้ง 3 คอลัมน์อยู่ในส่วนประกอบคอนเทนเนอร์

รหัสสำหรับส่วนประกอบคอนเทนเนอร์ -

HTML ของส่วนประกอบคอนเทนเนอร์

<template>
   <div>
    <lightning-layout>
            <lightning-layout-item size="4">
                    <c-ma-my-cases-list-view oncaseselect={handlecaseselect} ></c-ma-my-cases-list-view>
            </lightning-layout-item>
            
            <lightning-layout-item size="4">
                    <c-ma-my-cases-comment selectedcaseid={selectedcaseid}></c-ma-my-cases-comment>
            </lightning-layout-item>

            <lightning-layout-item size="4">
                   <c-ma-my-cases-detail selectedcaseid={selectedcaseid}></c-ma-my-cases-detail>                  
            </lightning-layout-item>

    </lightning-layout>
  </div>
</template>

JS ของส่วนประกอบคอนเทนเนอร์

import { LightningElement, track } from 'lwc';

export default class MaMyCasesContainer extends LightningElement {
    selectedcaseid;

    handlecaseselect(event){
        const caseId = event.detail;
        this.selectedcaseid = caseId;
    }
}

รายละเอียดของคอมโพเนนต์ซ้ายสุด - ดึงเคสจากเซิร์ฟเวอร์ในโครงสร้างข้อมูลที่กำหนดเองวนซ้ำและใช้คอมโพเนนต์ย่อยเพื่อแสดงแต่ละองค์ประกอบ

HTML ของคอมโพเนนต์ซ้ายสุด maMyCasesListView

<template>
    <div class="container slds-p-around_large slds-scrollable_y slds-theme_shade">
        <div>
            <!-- Server call successful logic-->
            <template if:true={caseswrapper}>
                <lightning-layout class="slds-grid slds-grid_vertical" horizontal-align="left">
                    <template for:each={caseswrapper} for:item="currentcase">

                        <c-ma-case-list-item
                            data-id={currentcase.caseId}
                            key={currentcase.caseId}
                            macasewrapper={currentcase}
                            onselect={handleSelect}
                        ></c-ma-case-list-item>
                        
                    </template>
                </lightning-layout>
            </template>
        </div>      
    </div>
</template>

JS ของคอมโพเนนต์ซ้ายสุด maMyCasesListView

import { LightningElement, wire, track, api } from 'lwc';
import getCaseList from '@salesforce/apex/MA_MyCasesController.getCaseWrapperList';

export default class MaMyCasesListView extends LightningElement {

    selectedcasewrapper;
    @track caseswrapper;
    @track error;
    @track isLoading;


    constructor(){
        super();
        this.isLoading = true;
        getCaseList()
            .then(result =>{
                this.caseswrapper = result;
                this.error = undefined;
                this.isLoading = false;
            })
            .catch(error => {
                this.error = error;
                this.caseswrapper = undefined
                this.isLoading = false;
                this.showErrorToast();
            })
    }

    handleSelect(event) {
        const caseId = event.detail;
        this.toggleListItems('selected', caseId);
        this.selectedcasewrapper = this.caseswrapper.find(
            (macase) => macase.caseId === caseId
        );
        const selectEvent = new CustomEvent('caseselect', {
            detail: caseId
        });
        // Fire the custom event
        this.dispatchEvent(selectEvent);
    }
}

ดังที่มองเห็นได้จาก html ขององค์ประกอบด้านบนมีองค์ประกอบภายในที่แสดงรายการแต่ละรายการ

HTML ของคอมโพเนนต์ที่แสดง maCaseListItem แต่ละรายการเคส

<template>
    <div onclick={handleClick}>
        <lightning-layout horizontal-align="left" class="slds-grid slds-grid_vertical slds-text-align--left">
            <lightning-layout-item horizontal-align="left" class="slds-text-align--left">
                <b>{macasewrapper.Subject}</b>
            </lightning-layout-item>
            <lightning-layout-item class="slds-text-align--left slds-m-top_small">
                <lightning-layout>
                    <lightning-layout-item  horizontal-align="left" size="4">{macasewrapper.Status}</lightning-layout-item>
                    <lightning-layout-item horizontal-align="right">{macasewrapper.CaseNumber}</lightning-layout-item>
                </lightning-layout>
            </lightning-layout-item>
        </lightning-layout>
    </div>   
</template>

JS ของคอมโพเนนต์ที่แสดงรายการเคสแต่ละรายการ maCaseListItem

import { LightningElement, api } from 'lwc';

export default class MaCaseListItem extends LightningElement {
    @api macasewrapper;
    
    handleClick(event) {

        const selectEvent = new CustomEvent('select', {
            detail: this.macasewrapper.caseId
        });
        // Fire the custom event
        this.dispatchEvent(selectEvent);
    }
}

คอมโพเนนต์ซ้ายสุดจะแสดงรายการเคส เมื่อผู้ใช้คลิกที่หนึ่งในนั้นเหตุการณ์ "caseselect" จะเริ่มทำงานโดยคอมโพเนนต์ maCaseListItem พร้อมด้วยรหัสเคสเป็นรายละเอียดเหตุการณ์ คอมโพเนนต์คอนเทนเนอร์จับมันในแฮนเดิลฟังก์ชันเลือกและเก็บไว้ในแอ็ตทริบิวต์ที่เลือกเคสid

ตอนนี้ส่วนประกอบคอนเทนเนอร์ส่งรหัสนี้ไปยังองค์ประกอบที่สองและสามเพื่อให้สามารถดึงข้อมูลเพิ่มเติมจากเซิร์ฟเวอร์และแสดงข้อมูลที่เกี่ยวข้องได้ ฉันจะข้ามองค์ประกอบที่สองเนื่องจากมันแสดงกรณี ไปที่ส่วนประกอบที่สามซึ่งมีปุ่มปิดเคส ฉันแค่เน้นไปที่ปุ่มปิดเคสและลบรหัสอื่น ๆ ออกเพื่อความกะทัดรัด

HTML ของคอมโพเนนต์คอลัมน์ที่สาม maMyCasesDetail

<template>
    <div if:true={localSelectedCaseId} >
    <div class="container slds-p-around_large slds-scrollable_y">
    <lightning-card>
        <!-- code to show status, created date etc -->
    </lightning-card>
    <lightning-card>
        <!-- code about showing attachments -->
    </lightning-card>
    <div class="slds-box slds-align_absolute-center slds-theme_default">
       <lightning-button variant="brand-outline" label="Close Case" title="Close the case" onclick={handleCloseClick}></lightning-button>
    </div>    
    </div> 
</div>
</template>

JS ของคอมโพเนนต์คอลัมน์ที่สาม maMyCasesDetail

import { LightningElement, api, track, wire } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
import closeCase from '@salesforce/apex/MA_MyCasesController.closeCase';

export default class MaMyCasesDetail extends LightningElement {


    @track localSelectedCaseId;

    @api
    get selectedcaseid() {
        return this.localSelectedCaseId;
    }
    set selectedcaseid(value) {
        this.localSelectedCaseId = value;
    }


    @wire(getRecord, { recordId: '$localSelectedCaseId', fields: [CASENUMBER_FIELD, CASESUBJECT_FIELD, STATUS_FIELD, DESCRIPTION_FIELD, 'Case.Owner.Name', 'Case.CreatedDate'] })
    wiredRecord({ error, data }) {
        //code that gets case details from server based on localSelectedCaseId
    }


    handleCloseClick(event) {
        if (this.localSelectedCaseId && this.status != this.label.CaseClosedStatus) {
            this.isLoaded = !this.isLoaded;
            closeCase({ caseId: this.localSelectedCaseId })
                .then(result => {
                    this.caseStatus = result.Status;
                    this.handleSuccess();
                    this.status = result.Status;
                    this.sendCloseCaseDetails();
                })
                .catch(error => {
                    var errorMsg = 'Unknown error';
                    if (error.body) {
                        errorMsg = error.body.message;
                        console.log(errorMsg);
                    }
                })
        } 
    }

    sendCloseCaseDetails() {
        var caseClosedDetails = {status:this.status, id:this.localSelectedCaseId};
        const closeEvent = new CustomEvent('close', {
            detail: caseClosedDetails
        });
        this.dispatchEvent(closeEvent);
    }

}

ดังนั้นเมื่อผู้ใช้คลิกที่ปุ่ม "ปิดเคส" เมธอดฝั่งเซิร์ฟเวอร์จะอัปเดตสถานะเป็น "ปิด" และส่งคืนเคสที่อัปเดต คอมโพเนนต์นี้เริ่มการทำงานของเหตุการณ์ที่มี id ของเคสและสถานะที่อัพเดต องค์ประกอบหลักสามารถจับได้ แต่หลังจากนั้นฉันดูเหมือนจะไม่สามารถทำงานได้ ผู้ปกครองสามารถส่งผ่านเป็นพารามิเตอร์ไปยังองค์ประกอบด้านซ้ายสุด

@AuraEnabled
public static Case closeCase(String caseId) {
    Case c = [Select Id, Status from Case where Id=:caseId];
    c.Status = "Closed";
    update c;
    return c;   
}

โค้ดฝั่งเซิร์ฟเวอร์ที่ได้รับรายการเคสเริ่มต้น (สำหรับคอมโพเนนต์ซ้ายสุด) คือ -

@AuraEnabled
public static List<CaseCommentWrapper> getCaseCommentWrapperList(String caseId){
    User currentUser= [select Id,timezonesidkey from user where Id =:UserInfo.getUserId()];
    List<CaseComment> allCaseComments = [Select Id, ParentId, IsPublished, CommentBody, CreatedById, CreatedBy.Name, CreatedDate, SystemModstamp, LastModifiedDate, LastModifiedById, IsDeleted From CaseComment where ParentId=:caseId ORDER BY CreatedDate ASC];
    List<CaseCommentWrapper> allCaseCommentWrapper = new List<CaseCommentWrapper>();
    for(CaseComment c : allCaseComments){
        String createdDateStringFormat = getDateInStringFormat(c.CreatedDate,currentUser.timezonesidkey);
        Boolean isCurrentUserAuthor = (currentUser.Id == c.CreatedById)?True:False;
        String caseCommentId = String.ValueOf(c.Id);
        allCaseCommentWrapper.add(new CaseCommentWrapper(caseCommentId,c.CommentBody, c.CreatedBy.Name, createdDateStringFormat,isCurrentUserAuthor));
    }
    return allCaseCommentWrapper;
}

public class CaseWrapper{
    @AuraEnabled
    public Id caseId;
    @AuraEnabled
    public String CaseNumber;
    @AuraEnabled
    public String Status;
    @AuraEnabled
    public String Subject;

    public CaseWrapper(Id caseId,String CaseNumber, String Status, String Subject){
        this.caseId = caseId;
        this.CaseNumber = CaseNumber;
        this.Status = Status;
        this.Subject = Subject;
    }
}

ดังนั้นคอลัมน์ที่สาม (คอลัมน์ขวาสุด) จึงเริ่มเหตุการณ์ที่มี id ของกรณีที่สถานะถูกอัปเดตและสถานะที่อัปเดตเป็นรายละเอียดเหตุการณ์

วิธีใดเป็นวิธีที่ดีในการอัปเดตสถานะของเคสนั้นบน HTML ของคอมโพเนนต์ซ้ายสุด ฉันได้ลองตำแหน่งที่องค์ประกอบหลักจัดการกับเหตุการณ์ตั้งค่าแอตทริบิวต์ 2 รายการในองค์ประกอบ maMyCasesListView (คอลัมน์ซ้ายสุด) - หนึ่งแอตทริบิวต์ที่แสดงถึงรหัสกรณี (localcasestatuschangeid) ซึ่งเป็นแอตทริบิวต์อื่นที่แสดงสถานะที่อัปเดต (localcasestatuschangeupdatedstatus) ฉันลองทำซ้ำในแต่ละรายการภายใน maMyCasesListView เพื่อดูว่า ID กรณีใดตรงกับและอัปเดตสถานะ แต่ไม่ปรากฏใน HTML

this.template.querySelectorAll('c-ma-case-list-item').forEach(item => {
    console.log('iterating through list');
    if (item.macasewrapper.caseId === this.localcasestatuschangeid) {
        console.log('case found');
        item.macasewrapper.Status = this.localcasestatuschangeupdatedstatus;
    }
});

แต่อย่างที่บอกว่ามันไม่แสดงบน HTML ฉันยังพบแนวคิดของการมี 2 แอตทริบิวต์ - localcasestatuschangeid และ localcasestatuschangeupdatedstatus - บนส่วนประกอบ maMyCasesListView ไม่สว่างมาก เหตุการณ์ที่จัดการโดย parent เป็นอ็อบเจ็กต์โดยที่ event.detail มีพารามิเตอร์มากมาย มีวิธีใดบ้างที่ฉันสามารถส่ง event.detail จากคอมโพเนนต์คอนเทนเนอร์ไปยัง maMyCasesListView จากนั้นให้ maMyCasesListView แตก id และสถานะจากในนั้นได้หรือไม่

คำตอบ

rahulgawale Aug 20 2020 at 07:33

มีสองวิธีที่คุณสามารถทำได้ แต่ก่อนหน้านั้นคุณต้องทำการเปลี่ยนแปลงเพียงครั้งเดียวคุณต้องเปลี่ยนgetCaseListวิธีการเป็นวิธีเอเพ็กซ์แบบมีสายแทนที่จะเรียกมันว่าจำเป็นในตัวสร้าง

caseListWired; // define new property

@wire(getCaseList)
getCaseListWired(value) {
    this.caseListWired = value;
    let { error, data } = value;
    if (error) {
        this.error = error;
        this.caseswrapper = undefined
        this.isLoading = false;
        this.showErrorToast();
    } else if (data) {
        this.caseswrapper = result;
        this.error = undefined;
        this.isLoading = false;
    }
}

refreshApexนอกจากนี้คุณจำเป็นต้องนำเข้า

import { refreshApex } from '@salesforce/apex';

ด้วยวิธีการแบบใช้สายคุณมีสองข้อได้เปรียบหนึ่งจะลดลงโทรเซิร์ฟเวอร์และอื่น ๆ refreshApexที่คุณสามารถฟื้นฟูแคชเมื่อใดก็ตามที่จำเป็นต้องใช้

แนวทาง 1

ขั้นตอนที่ 1

maMyCasesListViewวิธีการเขียนสาธารณะหนึ่งในองค์ประกอบคือด้านซ้ายสุด

@api refreshCaseList(){
    refreshApex(this.caseListWired); // this is the same attribute assigned in wired method.
}

ขั้นตอนที่ 2

จัดส่งเหตุการณ์จากองค์ประกอบด้านขวาสุดmaMyCasesDetailเมื่อเคสถูกปิดซึ่งคุณได้ทำไปแล้วในhandleCloseClickวิธีการ

const closeEvent = new CustomEvent('close');
this.dispatchEvent(closeEvent);

โปรดทราบว่าตอนนี้คุณไม่จำเป็นต้องส่งข้อมูลใด ๆ

ขั้นตอนที่ 3

คุณต้องจัดการเหตุการณ์นั้นในส่วนประกอบคอนเทนเนอร์และเรียกใช้วิธีสาธารณะซึ่งเรากำหนดไว้ในขั้นตอนที่ 1

 <c-ma-my-cases-detail selectedcaseid={selectedcaseid} onclose={handleCloseCase}></c-ma-my-cases-detail>

JS

handleCloseCase(){
    this.template.querySelector('c-ma-my-cases-list-view').refreshCaseList();
}

แนวทาง 2

คุณสามารถใช้เหตุการณ์ pub-sub เช่นLightning Messaging Serviceเพื่อเรียกใช้เมธอด refreshApex

ข้อเสนอแนะ

ในขณะที่คุณกำลังอัปเดตสถานะของเคสคุณใช้updateRecordfrom uiRecordApiเพื่ออัปเดตสถานะแทนวิธีการเอเพ็กซ์ที่กำหนดเอง

หากคุณไม่มีความต้องการเฉพาะคุณสามารถกำจัด Wrapper และส่งคืนรายการเคสในgetCaseListวิธีการ