Удаление/добавление записей в LWC datatable

В предыдущей статье мы создавали LWC редактируемую таблицу. Для того, чтобы сделать работу с ней более приятной для пользователей, добавим пару кнопок.
Редактирование записей у нас уже есть, добавим удаление и, соответственно, добавление новых контактов.

У элемента lightning-datatable есть набор чекбоксов, позволяющих отмечать строки таблицы как выбранные. Будем использовать их для удаления контактов, по несколько штук за раз. Количество выделенных строк выведем, как число - просто потому, что почему бы и нет :slight_smile:

Итак, кнопка:

<div class="slds-col">
    <span><p style="margin-left: 5%">Selected Records: <b style="color:red;">{recordsCount}</b></p></span>
</div>
<div class="slds-col">
    <span>                    
        <lightning-button label="Delete"
                          icon-name="utility:delete"
                          disabled={isDeleteButtonDisabled}
                          variant="destructive" 
                          onclick={deleteContacts}
                          style="margin-left: 40%"></lightning-button>
    </span>
</div>

Переменную isDeleteButtonDisabled будем использовать для того, чтобы избавить пользователей от искушения нажать большую красивую кнопку, когда этого делать не нужно.
Также, из новых переменных в нашем .js файле нам пригодятся

@track recordsCount = 0;

и

selectedRecords = [];

При нажатии на чекбоксы, в нашей таблице необходимо понимать, какие же строки были выбраны, чтобы в дальнейшем с ними работать:

getSelectedRecords(event) {
        const selectedRows = event.detail.selectedRows;        
        this.recordsCount = event.detail.selectedRows.length;
        let conIds = new Set();

        for (let i = 0; i < selectedRows.length; i++) {
            conIds.add(selectedRows[i].Id);
        }
        this.selectedRecords = Array.from(conIds);
        if(this.recordsCount > 0){
            this.isDeleteButtonDisabled = false;
        }
        else{
            this.isDeleteButtonDisabled = true;
        }
    }

После того как строки для удаления выбраны, пользователь нажимает кнопку и выполняется метод:

deleteContacts(){
        if(this.selectedRecords){
            let promises = new Set();
            for(let i = 0; i < this.selectedRecords.length; i++){
                promises.add(deleteRecord(this.selectedRecords[i])); // deleteRecords - метод из uiRecordApi, его нужно заимпортить в начале
            }
        
            Promise.all(promises).then(records =>{
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Success',
                        message: 'Contact deleted',
                        variant: 'success'
                    })
                );                
                this.selectedRecords = [];
                this.isDeleteButtonDisabled = true;
                return refreshApex(this.contacts);
            }).catch(error => {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error deleting record',
                        message: error.body.message,
                        variant: 'error'
                    })
                );
            }); 
        }
    }

Выбранные контакты удалены.

С добавлением нового контакта всё несколько интереснее.
С кнопкой всё понятно:

<lightning-button label="Add"
                  icon-name="utility:add"
                  variant="brand" 
                  onclick={addContact}></lightning-button>

А вот в контроллере просто взять и создать запись не получится, ведь пользователю нужно каким-от образом ввести данные. Где-то на просторах интернета видел, советовали показывать модальное окно с полями для ввода и отдельной кнопкой “Сохранить”, потом вставлять запись и обновлять таблицу. Но писать всё это ради такого, казалось бы, обыденного функционала мне откровенно лень. К тому же, в нашем распоряжении такая распрекрасная таблица есть :slight_smile:
Так добавим же в неё строку, которую можно будет редактировать, как и все остальные, исходя из уже имеющихся свойств таблицы:

addContact(){
    let newContact = {Id:"",FirstName:"",LastName:"",Title:"",Phone:"",Email:""}
    this.contacts.data = [...this.contacts.data, newContact];
}

В таблицу добавится строка с пустыми значениями, пользователь введёт данные. Обрабатываться они будут по нажатию на кнопку “Save”, как и простое редактирование. Значит метод handleSave, который я приводил в статье, необходимо доработать:

handleSave(event) {

        const recordInputs =  event.detail.draftValues.slice().map(draft => {
            const fields = Object.assign({}, draft);            
            return { fields };
        });
        
        let promises = new Set();
        for(let i = 0; i < recordInputs.length; i++){
            if(JSON.stringify(recordInputs[i].fields.Id).includes('row-')){
                delete recordInputs[i].fields.Id;
                recordInputs[i].fields.AccountId = this.recordId;
                recordInputs[i].apiName = CONTACT_OBJECT.objectApiName;
                promises.add(createRecord(recordInputs[i]))
            }
            else{              
                promises.add(updateRecord(recordInputs[i]));
            }
        }

        Promise.all(promises).then(records => {
            this.dispatchEvent(
                new ShowToastEvent({
                    title: 'Success',
                    message: 'Contacts updated',
                    variant: 'success'
                })
            );
            this.draftValues = [];
            return refreshApex(this.contacts);
        }).catch(error => {
            this.dispatchEvent(
                new ShowToastEvent({
                    title: 'Error updating record',
                    message: error.body.message,
                    variant: 'error'
                })
            );
        });        
    }

lightning-datatable при отображении записей на странице оперирует Id этих записей. В нашем случае с созданием новой строки никакого Id записи у нас, естественно, нет. Но lightning-datatable присваивает строке свой уникальный Id в формате: “Id”:“row-Х”, где Х - порядковый номер строки, у которой не оказалось айдишника СФ.
uiRecordApi метод createRecord не принимает объект с параметром Id. В то же время, apiName объекта обязательно нужно указать, что мы и делаем в теле if-а.

Обновление работает как и работало.

Пы.Сы. Стоит обратить внимание на то, что uiRecordApi методы обращаются к серверу, используя общие лимиты организации. Для организаций ниже Unlimited edition-а суточный лимит на вызовы АПИ извне составляет 15000 + (1000 х кол-во лицензий). Каждая запись в нашем примере создаётся/обновляется/удаляется отдельным асинхронным вызовом СФ АПИ.
В качестве альтернативы, можно было бы использовать 1 вызов за раз (ну ладно, 2. Второй - для обновления таблицы), передавая JSON со списком контактов в апекс метод, который бы его распарсил и сделал всё что нужно уже на стороне сервера.