Building A CRUD Application with Svelte
Svelte is a modern JavaScript compiler that allows you to write easy-to-understand JavaScript code that is then compiled to highly efficient code that runs in the browser.
The framework is similar to React or Vue, but in Svelte, we don’t have any dependencies. That means it will not take any time to interpret our code, so we will get pure JavaScript before run-time.
All you have to do is to write your code in normal JavaScript with some svelte concept and svelte will compile that code to a highly optimised JavaScript code bundle that runs directly in the browser.
In this article, we will learn some major concepts in svelte by building a A CRUD Application with Svelte (note-taking app). We will start by creating a svelte cli project.
We will create a new directory called svelte-project
in our home directory using our terminal for the project.
cd desktop
mkdir svelte-project && cd svelte-project
npx degit sveltejs/template my-svelte-project
cd my-svelte-project
npm install
code . && npm run dev
The first and second line of code creates a new directory in our home directory called svelte-project
and then we move into that directory.
The third line of code in our terminal creates a svelte project called my-svelte-project ,
after that, we move into the project directory using cd my-svelte-project
and run npm
install to download the additional dependencies of the template.
We’re now ready to run our Svelte site in development mode by running npm run dev
and open it up in visual studio code using the code .
command.
This will start the app on localhost, on port 5000, by default:
Accessing http://localhost:5000
on our browser, you’ll see the “Hello world!” example:
Let’s go to our code editor and start modifying the app. Taking a look at the package.json
file reveals two things.
The first is that Svelte uses Rollup by default for module bundling which is an alternative for “webpack”. If desired, it can be changed to use Webpack or Parcel. The second is that Svelte apps have no required runtime dependencies, only devDependencies.
The most important starting files in this project are:
- src/main.js
- src/App.svelte
- public/index.html
- The file public/index.html contains the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Svelte app</title>
<link rel='icon' type='image/png' href='/favicon.png'>
<link rel='stylesheet' href='/global.css'>
<link rel='stylesheet' href='/build/bundle.css'>
<script defer src='/build/bundle.js'></script>
</head>
<body>
</body>
</html>
Note how it calls in two CSS files and one JavaScript file from the same public directory. The global.css
holds a global CSS that can affect any component.The build/bundle.css is generated from the CSS in each component. The build/bundle.js
is generated from the JavaScript and HTML in each component and any other JavaScript in the app.
2. The file src/main.js contains the following:
import App from './App.svelte';
const app = new App({
target: document.body,
props: {
name: 'world'
}
});
export default app;
This renders the App
component. The target
property specifies where the component should be rendered. For most apps, this is the body of the document. The name
prop is passed to the App component.
3. The file src/App.svelte contains the following:
<script>
export let name;
</script>
<main>
<h1>Hello {name}!</h1>
<p>Visit the <a href="https://svelte.dev/tutorial">Svelte tutorial</a> to learn how to build Svelte apps.</p>
</main>
<style>
main {
text-align: center;
padding: 1em;
max-width: 240px;
margin: 0 auto;
}
h1 {
color: #ff3e00;
text-transform: uppercase;
font-size: 4em;
font-weight: 100;
}
@media (min-width: 640px) {
main {
max-width: none;
}
}
</style>
The exported variables in the script tag is the prop gotten from the src/main.js
files. Curly braces are used to output the value of a JavaScript expression. This is referred to as interpolation. The style tag holds all the css styles that is scoped to this particular component.
Building A Note Taking App
Let’s start by building a simple user interface for our application.We will be using bootstrap for the user interface. We will be adding the bootstrap CDN
in the public/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Svelte app</title>
<link rel="icon" type="image/png" href="/favicon.png" />
<link rel="stylesheet" href="/global.css" />
<link rel="stylesheet" href="/build/bundle.css" />
<!-- boostarp cdn starts here -->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
crossorigin="anonymous"
/>
<!-- bootstrap cdn ends here -->
<script defer src="/build/bundle.js"></script>
<style>
body {
background: #f2f4f8;
}
</style>
</head>
<body></body>
</html>
With the bootstrap CDN we can make use of all the bootstrap classes.Let’s start building a user interface for our application. Replace the codes in src/App.svelte
with this:
<script>
</script>
<style>
@import url("https://fonts.googleapis.com/css?family=Nunito&display=swap");
* {
font-family: "Nunito", sans-serif;
}
</style>
<section>
<div class="container">
<div class="row mt-5">
<div class="col-md-6">
<div class="card p-2 shadow">
<div class="card-body">
<h5 class="card-title mb-4">Add New Note</h5>
<form>
<div class="form-group">
<label for="title">Title</label>
<input
type="text"
class="form-control"
id="text"
placeholder="Note Title" />
</div>
<div class="form-group">
<label for="category">Category</label>
<select class="form-control" id="category">
<option selected disaabled>Selecet a category</option>
<option>School</option>
<option>School</option>
<option>School</option>
</select>
</div>
<div class="form-group">
<label for="content">Content</label>
<textarea
class="form-control"
id="content"
rows="3"
placeholder="Note Content" />
</div>
<button type="submit" class="btn btn-primary">Add Note</button>
</form>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">Category</div>
<div class="card-body">
<h5 class="card-title">Title of Note</h5>
<p class="card-text">Some description of this note</p>
<button class="btn btn-info">Edit</button>
<button class="btn btn-danger">Delete</button>
</div>
</div>
</div>
</div>
</div>
</section>
After replacing the code in src/App.svelte
with the code above, access http://localhost:5000
on your browser and you should get this:
Now that we have a UI for our application, let’s create an array of dummy notes in the script section:
<script>
let notes = [
{
id: 1,
title: "Sweetest framework ever",
category: "Church",
content: "This is the content of this note"
},
{
id: 2,
title: "intro to svelt",
category: "School",
content:
"This could be an intro to svelt,so you need to keep calm and see the magic"
}
];
</script>
Let’s display this array of notes in our application,so to do that we have to loop over it. Svelte has a special concept for looping over items in an array. So to display the dummy notes, we need to use the svelte each
block.Edit the card that displays the note to this:
{#each notes as note}
<div class="card mb-3">
<div class="card-header">{note.category}</div>
<div class="card-body">
<h5 class="card-title">{note.title}</h5>
<p class="card-text">{note.content}</p>
<button class="btn btn-info">Edit</button>
<button class="btn btn-danger">Delete</button>
</div>
</div>
{/each}
Now our application should look like this:
The notes displayed there is now gotten from the array of notes.
Creating A New Note
To create a new note we need to create a variable that will hold the inputted note details and then bind the variable to the input field. For this purpose, the bind:value="<data_property>"
syntax is used. Also, a shorthand syntax is valid bind:value
if name of the data property coincides with the name of a DOM element attribute. After this, we need to create a function that will add the inputted note to our notes array and then update the DOM.
Let’s create a variable that will hold our inputted values:
let data = {
title: "",
category: "",
content: "",
id: null
};
And then lets update our form to this:
<form>
<div class="form-group">
<label for="title">Title</label>
<input
bind:value={data.title}
type="text"
class="form-control"
id="text"
placeholder="Note Title" />
</div>
<div class="form-group">
<label for="category">Category</label>
<select
class="form-control"
id="category"
bind:value={data.category}>
<option selected disaabled>Selecet a category</option>
<option value="School">School</option>
<option value="Church">Church</option>
<option value="Home">Home</option>
</select>
</div>
<div class="form-group">
<label for="content">Content</label>
<textarea
bind:value={data.content}
class="form-control"
id="content"
rows="3"
placeholder="Note Content" />
</div>
<button type="submit" class="btn btn-primary">Add Note</button>
</form>
Now we need to add an event listener to create a new note. In svelte we can listen to any event on an element with the on: directive. So let’s add an event listener to the Add Note
button like this:
<button type="submit" on:click|preventDefault={addNote} class="btn btn-primary">Add Note</button>
The preventDefault
is a modifier that alters the behavior of the DOM. Now when the form is submitted it call a function addNote
.The function will add a new note to our notes array.
let addNote = () => {
const newNote = {
id: notes.length + 1,
title: data.title,
category: data.category,
content: data.content
};
notes = notes.concat(newNote);
data = {
id: null,
title: "",
category: "",
content: ""
};
console.log(notes);
};
Deleting A Note
To delete a note, let’s add an event listener to the delete button to call a function deleteNote
.We will pass a parameter in the delete function to get the note id which is a unique identifier for each note:
<button class="btn btn-danger" on:click={deleteNote(note.id)}>Delete</button>
The delete function:
let deleteNote = id => {
console.log(id);
notes = notes.filter(note => note.id !== id);
};
Updating A Note
To update a note, we, first of all, create a variable `isEdit` and set it to false by default. Then we add an event listener to the edit button which calls a function `editNote` with the selected note passed as a parameter like this:
<button class="btn btn-info" on:click={editNote(note)}> Edit</button>
This function will reassign the `isEdit` variable to true when it is clicked. We then assign the data object which is bonded to our form input fields to the note that was passed as a parameter in the `editNote` function:
let isEdit = false;
let editNote = note => {
isEdit = true;
data = note;
};
Now we want a situation whereby when the Edit button is clicked the Add note button on the form is changed to Edit Note. We can do this by making use of svelte if/else block.
So lets edit the Add note button to this:
{#if isEdit === false}
<button type="submit" on:click|preventDefault={addNote} class="btn btn-primary">
Add Note</button>
{:else}
<button type="submit" on:click|preventDefault={updateNote} class="btn btn-info">
Edit Note</button>
{/if}
The Edit Note button calls a function updateNote
to update the selected note to what the user has changed it to:
let updateNote = () => {
isEdit = !isEdit;
let noteDB = {
title: data.title,
category: data.category,
content: data.content,
id: data.id
};
let objIndex = notes.findIndex(obj => obj.id == noteDB.id);
console.log("Before update: ", notes[objIndex]);
notes[objIndex] = noteDB;
data = {
id: null,
title: "",
category: "",
content: ""
};
};
Now that we have completed our CRUD application,our App.svelte
file should look like this:
<script>
let notes = [
{
id: 1,
title: "Sweetest framework ever",
category: "Church",
content: "This is the content of this note"
},
{
id: 2,
title: "intro to svelt",
category: "School",
content:
"This could be an intro to svelt,so you need to keep calm and see the magic"
}
];
let data = {
title: "",
category: "",
content: "",
id: null
};
let addNote = () => {
const newNote = {
id: notes.length + 1,
title: data.title,
category: data.category,
content: data.content
};
notes = notes.concat(newNote);
data = {
id: null,
title: "",
category: "",
content: ""
};
console.log(notes);
};
let isEdit = false;
let editNote = note => {
isEdit = true;
data = note;
};
let updateNote = () => {
isEdit = !isEdit;
let noteDB = {
title: data.title,
category: data.category,
content: data.content,
id: data.id
};
let objIndex = notes.findIndex(obj => obj.id == noteDB.id);
console.log("Before update: ", notes[objIndex]);
notes[objIndex] = noteDB;
data = {
id: null,
title: "",
category: "",
content: ""
};
};
let deleteNote = id => {
console.log(id);
notes = notes.filter(note => note.id !== id);
};
</script>
<style>
@import url("https://fonts.googleapis.com/css?family=Nunito&display=swap");
* {
font-family: "Nunito", sans-serif;
}
</style>
<section>
<div class="container">
<div class="row mt-5 ">
<div class="col-md-6">
<div class="card p-2 shadow">
<div class="card-body">
<h5 class="card-title mb-4">Add New Note</h5>
<form>
<div class="form-group">
<label for="title">Title</label>
<input
bind:value={data.title}
type="text"
class="form-control"
id="text"
placeholder="Note Title" />
</div>
<div class="form-group">
<label for="category">Category</label>
<select
class="form-control"
id="category"
bind:value={data.category}>
<option selected disaabled>Selecet a category</option>
<option value="School">School</option>
<option value="Church">Church</option>
<option value="Home">Home</option>
</select>
</div>
<div class="form-group">
<label for="content">Content</label>
<textarea
bind:value={data.content}
class="form-control"
id="content"
rows="3"
placeholder="Note Content" />
</div>
{#if isEdit === false}
<button
type="submit"
on:click|preventDefault={addNote}
class="btn btn-primary">
Add Note
</button>
{:else}
<button
type="submit"
on:click|preventDefault={updateNote}
class="btn btn-info">
Edit Note
</button>
{/if}
</form>
</div>
</div>
</div>
<div class="col-md-6">
{#each notes as note}
<div class="card mb-3">
<div class="card-header">{note.category}</div>
<div class="card-body">
<h5 class="card-title">{note.title}</h5>
<p class="card-text">{note.content}</p>
<button class="btn btn-info" on:click={editNote(note)}>
Edit
</button>
<button class="btn btn-danger" on:click={deleteNote(note.id)}>
Delete
</button>
</div>
</div>
{/each}
</div>
</div>
</div>
</section>
Conclusion
That’s it…Svelte is a worthy alternative to the currently popular options of React, Vue, and Angular. It has many benefits, including small bundle sizes, simple component definitions, easy state management, and reactivity without a virtual DOM.
you can access CodeSource from here.