How to Build a Simple CSV Parser with Lightning Web Components

 


Create an LWC to upload a CSV File in Salesforce and show its Contents in a Lightning Datatable


Introduction

In Salesforce projects, you frequently have to allow Excel lists or CSV files to be uploaded and create data records based on their contents.

The use of third-party libraries is then frequently considered in these situations. External libraries, on the other hand, are difficult to use in Salesforce because they can only be used as static resources and lack reasonable versioning.

Notwithstanding, contingent upon the intricacy of the necessities, composing a CSV parser with Vanilla JavaScript with little effort is likewise conceivable. Therefore, that is exactly what we intend to do in this post: simply construct our own LWC CSV parser.

Uploading a CSV File

We’ll start with the most important, providing the file upload to be able to make our CSV file available for parsing its contents. Fortunately, Salesforce provides a standard component that we can use for this purpose in our LWC - Lightning Input. So let’s just add an input field of type “file” to the HTML template of our LWC to handle incoming file uploads. We configure the upload to only accept CSV files.
	
 <template>
    <lightning-card title="CSV To Datatable" icon-name="doctype:csv">
        <div class="slds-p-around_medium">
            <lightning-input type="file" label="Please upload a UTF-8 encoded, comma separated .csv file" accept=".csv"
                onchange={handleCSVUpload}>
            </lightning-input>
        </div>

        <div style="height: 500px">
            <lightning-datatable key-field="Id" data={data} columns={columns} hide-checkbox-column>
            </lightning-datatable>
        </div>
    </lightning-card>
</template>
    


In the JS part of our LWC we will then use the FileReader object from the File API to read CSV uploads and make them available for further processing as follows:

	
import { LightningElement } from 'lwc';

export default class CreateCSVTotable extends LightningElement {
    columns =[];
    data = [];
    handleCSVUpload(event) {
        const files = event.detail.files;
        //console.log("#### files = "+JSON.stringify(files));
        if (files.length > 0) {
            const file = files[0];

            // start reading the uploaded csv file
            this.read(file);
        }
    }

    async read(file) {
        try {
            const result = await this.load(file);
            //console.log("#### result = "+JSON.stringify(result));
            // execute the logic for parsing the uploaded csv file
            this.parseCSV(result);
        } catch (e) {
            this.error = e;
        }
    }

    async load(file) {
        return new Promise((resolve, reject) => {
            const reader = new FileReader();

            reader.onload = () => {
                //console.log("#### reader.result = "+JSON.stringify(reader.result));
                resolve(reader.result);
            };
            reader.onerror = () => {
                //console.log("#### reader.error = "+JSON.stringify(reader.error));
                reject(reader.error);
            };
            //console.log("#### file = "+JSON.stringify(file));
            reader.readAsText(file);
        });
    }

    parseCSV(csv) {
        // parse the csv file and treat each line as one item of an array
        const lines = csv.split(/\r\n|\n/);
        //console.log("#### lines = "+JSON.stringify(lines));
        // parse the first line containing the csv column headers
        const headers = lines[0].split(',');
        console.log("#### headers = "+JSON.stringify(headers));
        // iterate through csv headers and transform them to column format supported by the datatable
        this.columns = headers.map((header) => {
          return { label: header, fieldName: header };
        });
        //console.log("#### this.columns = "+JSON.stringify(this.columns));
        const data = [];
        
        // iterate through csv file rows and transform them to format supported by the datatable
        lines.forEach((line, i) => {
          if (i === 0) return;
      
          const obj = {};
          const currentline = line.split(',');
      
          for (let j = 0; j < headers.length; j++) {
            obj[headers[j]] = currentline[j];
          }
      
          data.push(obj);
        });
        //console.log("#### data = "+JSON.stringify(data));
        // assign the converted csv data for the lightning datatable
        this.data = data;
      }
}
    
In most cases, the file upload would also allow for the upload of multiple files. In our use case, however, we only want to allow uploading and reading of a single file. In order to only take into account the first file in the list, we use files[0] in our code.

Building the CSV Parser

Now that we understand how to upload and read a CSV file in general, we can build the parser.

The necessary parsing method will basically just go through the data in our CSV file and turn it into a data structure that our code can use more easily later. We already convert our data into a format that can be easily displayed in a table because we want to show it to the user later. The most crucial steps are described in the comments that follow the code:

    
    parseCSV(csv) {
        // parse the csv file and treat each line as one item of an array
        const lines = csv.split(/\r\n|\n/);
        //console.log("#### lines = "+JSON.stringify(lines));
        // parse the first line containing the csv column headers
        const headers = lines[0].split(',');
        console.log("#### headers = "+JSON.stringify(headers));
        // iterate through csv headers and transform them to column format supported by the datatable
        this.columns = headers.map((header) => {
          return { label: header, fieldName: header };
        });
        //console.log("#### this.columns = "+JSON.stringify(this.columns));
        const data = [];
        
        // iterate through csv file rows and transform them to format supported by the datatable
        lines.forEach((line, i) => {
          if (i === 0) return;
      
          const obj = {};
          const currentline = line.split(',');
      
          for (let j = 0; j < headers.length; j++) {
            obj[headers[j]] = currentline[j];
          }
      
          data.push(obj);
        });
        //console.log("#### data = "+JSON.stringify(data));
        // assign the converted csv data for the lightning datatable
        this.data = data;
      }
    
    
We have now processed and prepared our data for further action using the aforementioned code. Therefore, their UI representation should come next. 

Displaying CSV Contents in a Datatable 

In the end, we want to present the user with the CSV processing results. We'll use Lightning Datatable, another standard LWC, for this. Our CSV data were already formatted according to the datatable component's specifications in the preceding section. As a result, the only thing that remains to be done is to incorporate the datatable component into our HTML template and to assign the appropriate values to the attributes of the data and columns. We also simply add a Lightning Card as a wrapper to our component to give it a formatted appearance that is a little bit more professional.

      
      <lightning-datatable key-field="Id" data={data} columns={columns} hide-checkbox-column>
            </lightning-datatable>
      
      



Finally, our table should now display the uploaded CSV file contents in the following format:



In principle, our component can now be utilized anywhere in our organization. The following XML file would be suitable for use on our LWC's App, Home, and Record Pages:

      
      <?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>55.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>
      
      
      

Conclusion

We have developed a straightforward CSV upload component that permits us to read the uploaded file's contents. A Lightning Datatable was also added to the UI to display the parsing results.

However, in this state, it would not be sufficient for a use case in a real project because it is only the beginning. The appropriate business logic for the actual processing of the data would now need to be added in order for our component to be utilized in such a setting. For instance, by including an Apex class that transforms the contents of the file into actual records for a particular kind of org object.

Additionally, our component only currently supports comma-separated CSV files encoded with UTF-8. Naturally, the delimiter may vary between CSV files. Therefore, it would also be a useful extension of our component if the user could set the separator dynamically, perhaps through an additional input field, in order to create a little bit more flexibility in this area.

On GitHub, you can find the entire source code, - https://github.com/avnishyadav25/CSV-Parser-with-LWC


Thanks.
Keep Reading.


Next Post Previous Post
No Comment
Add Comment
comment url