Svelte is the new big thing in the market and I decided to try one common use case i.e. CSV generation from JSON. For those who don't know svelte
"Svelte is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue do the bulk of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app."
There are several ways to setup Svelte project. You can read more about the many ways to get started here. For the purpose of this demo, we will be working with degit
which is a software scaffolding tool. To start, run the following command:
npx degit sveltejs/template svelte-CSV-demo
Now go inside the project directory using following command:
cd svelte-CSV-demo
let's install the project dependencies using following command:
npm install
Now our Svelte base project is ready. Let's start writing our code.
We have four part of our project
- load the JSON from REST API
- Integrate the JSON with template
- Add style to project
- CSV generation utility
- End to End integration
- Deploying to the web With now
If you are interested only in Code you can checkout the code from below URL
https://github.com/karkranikhil/svelte-csv-demo
1. load the JSON from REST API
Go to App.svelte
file and remove the existing code with the below code
<script>
import { onMount } from "svelte";
let tableData = [];
onMount(async () => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts`);
tableData = await res.json();
console.log(tableData);
});
</script>
As shown above, we have imported the onMount
from svelte package.
onMount
is fired after the component is rendered. After that we have initialized the variable tableData
with an empty array.
Now we have defined the onMount
function and within that we have used the async & await .
-
async
functions returns a promise. -
async
functions use an implicit Promise to return its result. Even if you don’t return a promise explicitlyasync
function makes sure that your code is passed through a promise. - await blocks the code execution within the
async
function, of which it(await statement) is a part.
We have used Fetch API to get the JSON from the service. The Fetch API is a promise-based JavaScript API for making asynchronous HTTP requests in the browser. On successful calling of REST API we are storing the JSON in tableData
and printing it in console.
Let'run the project and see the console. To start the project run the following command.
npm run dev
once Above command run successfully navigate to http://localhost:5000/.
Open your developer console and you will see the following output.
If you look at the above image we are able to get the data successfully. Now we will go to next step and will see how to integrate it with HTML markup
2. Integrate the JSON with template
Now we already have our API data in tableData
variable. Now we will integrate the data using #each
iterator. Add the following code to App.svelte
below script
tag
<div class="container">
<div class="header">
<h1>CSV generation from JSON in Svelte</h1>
</div>
<div class="main">
<table>
<thead>
<tr>
{#each tableHeader as header}
<th>{header}</th>
{/each}
</tr>
</thead>
<tbody>
{#each tableData as item}
<tr>
<td>{item.userId}</td>
<td>{item.id}</td>
<td>{item.title}</td>
<td>{item.body}</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
Above we have created the div
with class container
that hold two child one with header
class another with main
class. In div
with header
class we are only showing the header of our app. In div
with main
class we are creating the table and within the table we are creating table header and table body using #each
block. #each
loops the data in markup.
We are using two loop one for header and another for the body. For table body we are using tableData
that contains the REST API response and for header we are using the tableHeader
variable that will create now under the script
tag.
let's define the tableHeader
below tableData
and initializing it with the array of custom header keys as shown below.
let tableHeader = ["User Id", "ID", "Title", "Description"];
Let's run the project again if it's stop otherwise go to browser and you will see the following output.
3. Add style to project
I have define some CSS to make our page look better. you can use it by adding the style
tag after the markup
<style>
.container {
max-width: 1140px;
margin: auto;
}
.header {
display: flex;
justify-content: space-between;
display: flex;
justify-content: space-between;
background: orange;
padding: 10px;
}
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td,
th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
button {
border: none; /* Remove borders */
color: white; /* Add a text color */
padding: 14px 28px; /* Add some padding */
cursor: pointer; /* Add a pointer cursor on mouse-over */
background-color: #4caf50;
height: fit-content;
}
h1 {
margin: 0px;
}
</style>
Now if you look at the output, it will look like as shown below
4.CSV generation Utility
Here is the key step in which we have wrote some Utility that will generate the csv based on some parameters. It works with all browser and even on all mobile phones.
So, let's create a new file csvGenerator.js
inside the src folder and paste the below code in it.
export const csvGenerator = (totalData,actualHeaderKey,headerToShow,fileName) => {
let data = totalData || null;
if (data == null || !data.length) {
return null;
}
let columnDelimiter = ",";
let lineDelimiter = "\n";
let keys = headerToShow;
let result = "";
result += keys.join(columnDelimiter);
result += lineDelimiter;
data.forEach(function(item) {
let ctr = 0;
actualHeaderKey.forEach(function(key) {
if (ctr > 0) result += columnDelimiter;
if (Array.isArray(item[key])) {
let arrayItem =
item[key] && item[key].length > 0
? '"' + item[key].join(",") + '"'
: "-";
result += arrayItem;
} else if (typeof item[key] == "string") {
let strItem = item[key] ? '"' + item[key] + '"' : "-";
result += strItem ? strItem.replace(/\s{2,}/g, " ") : strItem;
} else {
let strItem = item[key] + "";
result += strItem ? strItem.replace(/,/g, "") : strItem;
}
ctr++;
});
result += lineDelimiter;
});
if (result == null) return;
var blob = new Blob([result]);
if (navigator.msSaveBlob) {
// IE 10+
navigator.msSaveBlob(blob, exportedFilenmae);
} else if (navigator.userAgent.match(/iPhone|iPad|iPod/i)) {
var hiddenElement = window.document.createElement("a");
hiddenElement.href = "data:text/csv;charset=utf-8," + encodeURI(result);
hiddenElement.target = "_blank";
hiddenElement.download = fileName;
hiddenElement.click();
} else {
let link = document.createElement("a");
if (link.download !== undefined) {
// Browsers that support HTML5 download attribute
var url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", fileName);
link.style.visibility = "hidden";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
};
As shown above, we have created a function called csvGenerator. That takes four paramters as mentioned below
totalData - totalData is the JSON data to pass to CSV sheet
actualHeaderKey - This is the array of JSON key name that need to be used to pick up data from totalData
headerToShow - This is the array of custom name to show on header row of the csv file
fileName -Name of the file by which it get download with an extension of .csv
csvGenerator
function will take the input and generate the CSV output by looping the data and adding comma to each value.
5. End to End integration
Till now we are ready with table and csvGenerator. Let's connect both together.
First we need to import the csvGenerator
file to our App.svelte
. Add the following line below the onMount
import statement
import { csvGenerator } from "./csvGenerator";
Now we need a handler that will get called on click of button from markup and call our utility csvGenerator
. Add the following code below onMount
function
function downloadHandler() {
let tableKeys = Object.keys(tableData[0]); //extract key names from first Object
csvGenerator(tableData, tableKeys, tableHeader, "svelte_csv_demo.csv");
}
As shown above, we have created a function called downloadHandler
that will called on click of button and generate the CSV file of table data.
Let's create now a button on our template. Add the following code below the the h1 tag
<button on:click={downloadHandler}>Download</button>
and run the project and you will see the below output on your browser.
On click of download button it will download the CSV in your machine.
4. Deploying to the web With now
Install now
if you haven't already:
npm install -g now
Then, from within your project folder:
cd public
now deploy --name svelte-csv-demo
now
will deploy your code and generate a URL.
Deployed Url - https://svelte-csv-demo.karkranikhil.now.sh
Github - https://github.com/karkranikhil/svelte-csv-demo
Top comments (2)
Thank you very much Nikhil. Super useful.
How would the "csv part" work with nested json?
For example:
In the "table part" I am rendering a user name like this:
Suspect that will require significant changes in the csvGenerator.js - or is there a smarter way?
let tableKeys = ?
Very useful. Thanks.