Buat atribut pembungkus reaktif di LWC

Aug 19 2020

Saya akan menjelaskan lebih dari yang diperlukan tentang masalah yang saya hadapi, sehingga jika saya ada pendekatan yang sepenuhnya lebih baik untuk menerapkan apa yang saya lakukan, seseorang dapat menyarankannya.

Saya sedang membangun halaman komunitas kilat untuk menunjukkan kasus pengguna. Halaman dibagi menjadi 3 kolom. Kolom paling kiri menunjukkan semua kasus oleh pengguna (bersama dengan subjek, status dan nomor kasus), kolom tengah menunjukkan komentar tentang kasus yang dipilih di kolom paling kiri, kolom paling kanan menunjukkan sedikit lebih banyak detail kasus yang dipilih di kolom paling kiri.

Silakan lihat gambar di bawah ini.

Inti dari masalah ini adalah ketika seseorang, mengklik tombol "Tutup kasus" di kolom paling kanan, itu memanggil server untuk mengubah status menjadi "Tertutup". Ini berfungsi dengan baik, tetapi kemudian saya harus memperbarui status yang ditampilkan di kolom paling kiri, dan di mana saya mengalami masalah.

Berikut rincian implementasinya.

Masing-masing kolom adalah komponen dan ketiganya berada dalam komponen wadah.

Kode untuk komponen kontainer -

HTML komponen penampung

<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 komponen kontainer

import { LightningElement, track } from 'lwc';

export default class MaMyCasesContainer extends LightningElement {
    selectedcaseid;

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

Detail komponen paling kiri - Ini mengambil kasus dari server dalam struktur data khusus, mengulanginya dan menggunakan komponen turunan untuk menampilkan elemen individual

HTML komponen paling kiri 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 dari komponen paling kiri 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);
    }
}

Seperti yang terlihat dari html komponen di atas, ada komponen dalam yang menunjukkan item daftar individual.

HTML komponen yang menampilkan item daftar kasus individual 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 komponen yang menampilkan item daftar kasus maCaseListItem individu

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);
    }
}

Jadi komponen paling kiri menunjukkan daftar kasus. Saat pengguna mengklik salah satunya, peristiwa "caseelect" dijalankan oleh komponen maCaseListItem dengan ID kasus sebagai detail peristiwa. Komponen container menangkapnya di function handlecaseselect dan menyimpannya dalam atribut selectedcaseid.

Sekarang komponen kontainer meneruskan Id ini ke komponen kedua dan ketiga, sehingga mereka dapat mengambil lebih banyak informasi dari server dan menampilkan data yang relevan. Saya akan melewatkan komponen kedua, karena ini menunjukkan kasus. Mari pindah ke komponen ketiga, yang memiliki tombol Tutup case. Saya hanya fokus pada tombol tutup kasus dan menghapus kode lain agar singkat

HTML komponen kolom ketiga 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 komponen kolom ketiga 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);
    }

}

Jadi, ketika pengguna mengklik tombol "Tutup Kasus", metode sisi server memperbarui status menjadi "Tertutup", dan mengembalikan kasus yang diperbarui. Komponen ini mengaktifkan peristiwa yang berisi id kasus dan status yang diperbarui. Komponen induk dapat menangkapnya, tetapi setelah itu sepertinya saya tidak bisa menyelesaikan semuanya. Induk dapat meneruskannya sebagai parameter ke komponen paling kiri.

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

kode sisi server yang mendapatkan daftar kasus awal (untuk komponen paling kiri) adalah -

@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;
    }
}

Jadi yang ketiga (kolom paling kanan) mengaktifkan peristiwa yang memiliki id kasus yang statusnya diperbarui, dan status yang diperbarui sebagai detail acara.

Apa cara yang baik untuk memperbarui status kasus tertentu pada HTML komponen paling kiri? Saya telah mencoba di mana komponen induk menangani acara tersebut, menetapkan 2 atribut pada komponen maMyCasesListView (kolom paling kiri) - satu atribut yang menggambarkan kasus Id (localcasestatuschangeid), atribut lain yang menggambarkan status yang diperbarui (localcasestatuschangeupdatedstatus). Saya mencoba iterasi melalui setiap item daftar di dalam maMyCasesListView untuk melihat ID kasus mana yang cocok dan memperbarui status. Tapi itu tidak muncul di 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;
    }
});

Tapi seperti yang saya katakan, itu tidak muncul di HTML. Saya bahkan menemukan gagasan untuk memiliki 2 atribut - localcasestatuschangeid dan localcasestatuschangeupdatedstatus - pada komponen maMyCasesListView tidak terlalu cerah. Peristiwa yang ditangani oleh induk adalah sebuah objek, di mana peristiwa.detail memiliki banyak parameter. Apakah ada cara agar saya bisa melewatkan event.detail dari komponen container ke maMyCasesListView, dan kemudian memiliki id ekstrak maMyCasesListView dan status dari sana?

Jawaban

rahulgawale Aug 20 2020 at 07:33

Ada dua cara untuk melakukan ini, tetapi sebelum itu, Anda perlu membuat satu perubahan, Anda perlu mengubah getCaseListmetode ke metode apex berkabel alih-alih memanggilnya secara imperatif di konstruktor.

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;
    }
}

Selain itu, Anda perlu mengimpor refreshApex.

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

Dengan metode kabel, Anda memiliki dua keuntungan, satu mengurangi panggilan server, dan yang lainnya adalah Anda dapat menyegarkan cache kapan pun diperlukan refreshApex.

Pendekatan 1

Langkah 1

Tulis satu metode publik di komponen paling kiri yaitu maMyCasesListView.

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

Langkah 2

Mengirim peristiwa dari komponen paling kanan maMyCasesDetail, ketika kasus ditutup, yang telah Anda lakukan dalam handleCloseClickmetode.

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

Perhatikan bahwa, sekarang Anda tidak perlu mengirim data apa pun.

LANGKAH 3

Anda perlu menangani peristiwa itu di komponen penampung dan memanggil metode publik yang kita definisikan di langkah 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();
}

Pendekatan 2

Anda dapat menggunakan peristiwa pub-sub yaitu Layanan Pesan Lightning untuk memanggil metode refreshApex.

Saran

Karena Anda baru saja memperbarui status kasing, Anda menggunakan updateRecorddari uiRecordApiuntuk memperbarui status alih-alih metode apex khusus.

Jika Anda tidak memiliki kebutuhan khusus, Anda dapat menghilangkan pembungkus dan mengembalikan daftar kasus dalam getCaseListmetode.