DEV Community

Mitesh Kamat
Mitesh Kamat

Posted on

Another Todo app with Svelte

Introduction

This article is about getting started with svelteJS.

Svelte...yes another javascript framework which is gaining some fuss.

How to start with svelte??

Svelte REPL is the easiest way to begin.
Click the download button to save a svelte-app.zip file to your computer and uncompress it.

Open a terminal window and set the project up...

cd /path/to/svelte-app
npm install

...then start up a development server:

npm run dev

This will serve your app on localhost:5000 and rebuild it with Rollup every time you make a change to the files in svelte-app/src.

I know there are quite a few todo apps that you will come across enroute to learning Svelte. This is a just a small implementation in my way.

Let's begin...
If you navigate to your src folder, you would see two files:
1) main.js

import App from './App.svelte';

var app = new App({
    target: document.body
});

export default app;

2) App.svelte

<script>
 let name = 'world';
</script>

<h1>Hello {name.toUpperCase()}!</h1>

Now if you check localhost:5000, you would see this,

Hello WORLD!

Now, we will begin adding our code for todo app.
My use case:
1) display a list of todos
2) option to add new todo
3) clear the selected or completed todo
4) display the count for remaining todos.

Create a file named Todos.svelte and add the following content:

<script>
let todos = [
    {done: false, text: 'Learn React JS'},
    {done: false, text: 'Learn Angular 2+'},
    {done: false, text: 'Learn Svelte JS'},
    { done: false, text: 'finish Svelte tutorial' },
    { done: false, text: 'build an app' },
    { done: false, text: 'world domination' }
];
</script>

Firstly I need a list of todos, so I created an array which holds some list.
Now, I need to display this todos so I add some HTML

{#each todos as todo}
<div>
    <input type="checkbox" checked={todo.done}/>
    <span>{todo.text}</span>
</div>   
{/each}

The above code iterates over the list of todos, displaying a checkbox with text.
{#each}...{/each} is used for iteration.

Now we need an option to add new todo

<script>
....
let newTodo = ''; // to store the entered text

function add(){
    todos = [...todos, {done:false, text:newTodo}]; //concat new value to list
    newTodo = '';
}
</script>
<form on:submit|preventDefault={add}>
    <input type="text" value={newTodo} /> //storing value in newTodo
    <button type="submit">Add new</button>
</form>
...

I have used a form element to contain my option for adding a todo.
To mention any event handler, you would use on:eventname={eventhandler}
For example: on:click={handleClick} or on:submit={add}

While submitting a form , we need to prevent the default submit to stop page reload. This can be done in svelte using pipe followed by preventdefault

<form on:submit|preventDefault={add}>

Go to localhost try adding a new todo item and click add new button or press enter. You will notice the new item gets added to the list.

Time to clear the completed todo.

function clear(e){
    e.preventDefault();
    todos = todos.filter(todo => !todo.done)
}
</script>
<style>
    .done {
        opacity: 0.4;
    }
</style>
<form on:submit|preventDefault={add}>
    <input type="text" bind:value={newTodo} />
    <button type="submit" disabled={!newTodo}>Add new</button>
    <button on:click={clear}>Clear completed</button>
</form>
{#each todos as todo}
<div class:done={todo.done}>
    <input type="checkbox" bind:checked={todo.done}/>
    <span>{todo.text}</span>
</div>   
{/each}

I have added a class name done and used class:done to apply the class only on selected checkbox or todo.
I have also disabled the add new button when the input box is empty.
We have a clear button which on click removes the selected item(s) from the list.
Notice the bind: directive. It is like two way binding.

Last thing to do is to display the remaining todos.

Reactivity

At the heart of Svelte is a powerful system of reactivity for keeping the DOM in sync with your application state — for example, in response to an event.

We will use this reactive declaration:

$: remaining = todos.filter(todo => !todo.done).length;

Add something after " $: " which you want to test after any state change.

So, my final code for Todos.svelte looks like this:

<script>
let todos = [
    {done: false, text: 'Learn React JS'},
    {done: false, text: 'Learn Angular 2+'},
    {done: false, text: 'Learn Svelte JS'},
    { done: false, text: 'finish Svelte tutorial' },
    { done: false, text: 'build an app' },
    { done: false, text: 'world domination' }
];
let newText = '';

function add(){
    todos = [...todos, {done:false, text:newText}];
    newText = '';
}

function clear(e){
    e.preventDefault();
    todos = todos.filter(todo => !todo.done)
}

$: remaining = todos.filter(todo => !todo.done).length;
</script>
<style>
    .done {
        opacity: 0.4;
    }
</style>

<div>
<h2> A simple todo app with svelte</h2>
<form on:submit|preventDefault={add}>
    <input type="text" bind:value={newText} />
    <button type="submit" disabled={!newText}>Add new</button>
    <button on:click={clear}>Clear completed</button>
</form>
<p>you have {remaining} remaining {remaining === 1? 'task': 'tasks'}</p>
{#each todos as todo}
<div class:done={todo.done}>
    <input type="checkbox" bind:checked={todo.done}/>
    <span>{todo.text}</span>
</div>   
{/each}
</div>

See localhost:5000 and try adding new todos and clearing completed todos.

I tried a very simple use case and it is not styled. Just wanted to put forward basic working todo with minimal usecase.

Hope you followed it and got the desired result.
Cheers !!!

Discussion (0)