ทำให้แอตทริบิวต์ของ wrapper มีปฏิกิริยาใน LWC
ฉันจะอธิบายอย่างละเอียดเกินความจำเป็นในปัญหาที่ฉันกำลังเผชิญดังนั้นหากฉันมีแนวทางที่ดีกว่าในการนำสิ่งที่ฉันกำลังทำไปใช้มีใครบางคนสามารถแนะนำได้
ฉันกำลังสร้างหน้าชุมชน 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 และสถานะจากในนั้นได้หรือไม่
คำตอบ
มีสองวิธีที่คุณสามารถทำได้ แต่ก่อนหน้านั้นคุณต้องทำการเปลี่ยนแปลงเพียงครั้งเดียวคุณต้องเปลี่ยน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
ข้อเสนอแนะ
ในขณะที่คุณกำลังอัปเดตสถานะของเคสคุณใช้updateRecord
from uiRecordApi
เพื่ออัปเดตสถานะแทนวิธีการเอเพ็กซ์ที่กำหนดเอง
หากคุณไม่มีความต้องการเฉพาะคุณสามารถกำจัด Wrapper และส่งคืนรายการเคสในgetCaseList
วิธีการ