
Существует несколько стандартных вариантов для загрузки CSV-файла и создания записей, но иногда, в зависимости от бизнес-требований, вам потребуется индивидуальное и интуитивно понятное решение для загрузки файла и запуска пользовательского процесса для создания/обновления записей. В этом посте рассказывается, как загрузить CSV, проанализировать содержимое файла и создать записи учетной записи, а в конце отобразить простой отзыв для пользователя. Это очень простой LWC для разбора CSV-файла и создания записей, и он может стать отправной точкой для более надежного решения.

Я видел несколько сообщений, объясняющих, как анализировать файлы CSV, и были разные решения: анализ CSV с использованием метода Apex, компонентов ауры, LWC, методов Javascript, выполняющих некоторые простые операции анализа. В нашем случае мы будем использовать существующую библиотеку для разбора файла и облегчения нашей жизни: Papa Parse. Эта библиотека имеет хорошие методы и параметры конфигурации для анализа CSV-файла. Вы можете выбрать, что будет символом-разделителем, есть ли у файла заголовок или нет, заключены ли столбцы в кавычки, какой символ и так далее.
Так как эта библиотека принимает на вход объект Файл, мы будем использовать Lightning-ввод с типом файл, чтобы получить файл от пользователя. После выбора файла синтаксический анализатор Papa проанализирует файл и вернет список объектов, каждый из которых представляет собой строку из файла. Для использования библиотеки необходимо загрузить файл .js как статический ресурс и импортировать его в LWC:
import {LightningElement, track} from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import PARSER from '@salesforce/resourceUrl/PapaParse';
export default class ImportAccounts extends LightningElement {
parserInitialized = false;
renderedCallback() {
if(!this.parserInitialized){
loadScript(this, PARSER)
.then(() => {
this.parserInitialized = true;
})
.catch(error => console.error(error));
}
}
}
Вот HTML-часть LWC:
<template>
<lightning-card title="Import Accounts" icon-name="standard:account">
<lightning-spinner if:true={loading}></lightning-spinner>
<div class="slds-p-around_medium">
<lightning-input type="file"
label="CSV file"
multiple="false"
accept=".csv"
onchange={handleInputChange}></lightning-input>
</div>
<template if:true={rows.length}>
<div class="slds-p-around_medium">
<lightning-datatable key-field="key"
hide-checkbox-column
data={rows}
columns={columns}></lightning-datatable>
<div class="slds-p-around_small slds-align_absolute-center">
<lightning-button variant="neutral"
label="Cancel"
title="Cancel" onclick={cancel}
class="slds-m-left_x-small"></lightning-button>
<lightning-button variant="brand"
label="Create"
title="Create" onclick={createAccounts}
class="slds-m-left_x-small"></lightning-button>
</div>
</div>
</template>
</lightning-card>
</template>
При выборе файла будет выполняться следующая функция для разбора CSV-файла:
handleInputChange(event){
if(event.target.files.length > 0){
const file = event.target.files[0];
this.loading = true;
Papa.parse(file, {
quoteChar: '"',
header: 'true',
complete: (results) => {
this._rows = results.data;
this.loading = false;
},
error: (error) => {
console.error(error);
this.loading = false;
}
})
}
}
Атрибут complete – это функция обратного вызова, которая получает массив объектов со строками из CSV-файла. При этом у нас есть геттер для отображения строк в таблице данных молнии, чтобы пользователь мог предварительно просмотреть строки. Мы не ограничиваем количество отображаемых записей, но рассмотрите возможность сделать это, если вы собираетесь иметь дело с большим количеством записей. Добытчик:
get rows(){
if(this._rows){
return this._rows.map((row, index) => {
row.key = index;
if(this.results[index]){
row.result = this.results[index].id || this.results[index].error;
}
return row;
})
}
return [];
}
Не обращайте внимания на if(this.results[index]) на данный момент, его объяснение будет позже. Существует кнопка для создания записей учетной записи, загружаемых из CSV и отображаемых в таблице данных. Он вызовет следующую функцию:
createAccounts(){
const accountsToCreate = this.rows.map(row => {
const fields = {};
fields[NAME_FIELD.fieldApiName] = row.AccountName;
fields[DESCRIPTION_FIELD.fieldApiName] = row.Description;
const recordInput = { apiName: ACCOUNT_OBJECT.objectApiName, fields };
return createRecord(recordInput);
});
if(accountsToCreate.length){
this.loading = true;
Promise.allSettled(accountsToCreate)
.then(results => this._results = results)
.catch(error => console.error(error))
.finally(() => this.loading = false);
}
}
Здесь мы используем createRecordиз Lightning/uiRecordApiдля создания записей учетной записи и Promise.allSettledдля создания всех записей и получения результатов, которые будут отображаться в таблице данных вместе с записями. Вы должны были заметить, что мы используем (и определяем) только два столбца для создания записей (поля «Имя» и «Описание»). Вы можете изменить эту часть, чтобы принять больше столбцов, если это необходимо. В будущем я улучшу этот компонент, чтобы пользователь мог настраивать столбцы.
Мы определили геттер для преобразования результатов нужным нам образом, а затем использовали эту информацию вместе со строками, чтобы данные отображались пользователю. Для успешно созданных записей мы отображаем идентификаторы записей. При возникновении ошибки мы показываем сообщение об ошибке.
get results(){
if(this._results){
return this._results.map(r => {
const result = {};
result.success = r.status === 'fulfilled';
result.id = result.success ? r.value.id : undefined;
result.error = !result.success ? r.reason.body.message : undefined;
return result;
});
}
return [];
}
Вот весь контроллер JS:
import {LightningElement, track} from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import { createRecord } from 'lightning/uiRecordApi';
import PARSER from '@salesforce/resourceUrl/PapaParse';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import DESCRIPTION_FIELD from '@salesforce/schema/Account.Description';
export default class ImportAccounts extends LightningElement {
parserInitialized = false;
loading = false;
@track _results;
@track _rows;
get columns(){
const columns = [
{ label: 'Account Name', fieldName: 'AccountName' },
{ label: 'Description', fieldName: 'Description' }
];
if(this.results.length){
columns.push({ label: 'Result',fieldName: 'result' });
}
return columns;
}
get rows(){
if(this._rows){
return this._rows.map((row, index) => {
row.key = index;
if(this.results[index]){
row.result = this.results[index].id || this.results[index].error;
}
return row;
})
}
return [];
}
get results(){
if(this._results){
return this._results.map(r => {
const result = {};
result.success = r.status === 'fulfilled';
result.id = result.success ? r.value.id : undefined;
result.error = !result.success ? r.reason.body.message : undefined;
return result;
});
}
return [];
}
renderedCallback() {
if(!this.parserInitialized){
loadScript(this, PARSER)
.then(() => {
this.parserInitialized = true;
})
.catch(error => console.error(error));
}
}
handleInputChange(event){
if(event.target.files.length > 0){
const file = event.target.files[0];
this.loading = true;
Papa.parse(file, {
quoteChar: '"',
header: 'true',
complete: (results) => {
this._rows = results.data;
this.loading = false;
},
error: (error) => {
console.error(error);
this.loading = false;
}
})
}
}
createAccounts(){
const accountsToCreate = this.rows.map(row => {
const fields = {};
fields[NAME_FIELD.fieldApiName] = row.AccountName;
fields[DESCRIPTION_FIELD.fieldApiName] = row.Description;
const recordInput = { apiName: ACCOUNT_OBJECT.objectApiName, fields };
return createRecord(recordInput);
});
if(accountsToCreate.length){
this.loading = true;
Promise.allSettled(accountsToCreate)
.then(results => this._results = results)
.catch(error => console.error(error))
.finally(() => this.loading = false);
}
}
cancel(){
this._rows = undefined;
this._results = undefined;
}
}
В своем следующем сообщении в блоге я улучшу этот компонент, чтобы динамически получать поля из объекта Account и позволять пользователю сопоставлять столбцы из CSV-файла с полями. Быть в курсе!