Would You Make A Svelte Todo App?
Tags: javascript, svelte, webdev, beginners
Published: 2022-05-17 11:26:41

## What Is Svelte?

Svelte is a new javascript tool created by Rich Harris that is easy to learn and use for building fast web applications. However, it's unique compared to other frameworks where it doesn't work with concepts like virtual DOM.

In my opinion the best way to get started is to follow this tutorial and get started with writing Svelte. If you get stuck along the way then [fork](https://github.com/JavascriptDon/SvelteTodoApp) the SvelteTodoApp hosted on GitHub. 

## Creating a Svelte Project

We will start this tutorial by initialising our new project with the following command:

```
# Creating a new project
npx degit sveltejs/template 

# Install the dependencies...
npm install
```
...then start Rollup. 

```
npm run dev
```
Navigate to localhost:8080. You should see your app running. Edit a component file in src, save it, and reload the page to see your changes.

## Adding Bootstrap

I encourage you to add Sveltstrap using one of the following methods:

```
# Method One: Install sveltestrap and peer dependencies via NPM
  
 npm install sveltestrap 

# Method Two: Include CDN within Static HTML layout

<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css">
</head>

```

## Folder Structure

On the root level, we will create the following files

- Todo.svelte
- TodoItem.svelte

and modify App.svelte. 

## Our App Component

To keep things simple we will create the `Todo.svelte` and `TodoItem.svelte` files with the following structure. 

```
<script>
<!-- JavaScript Logic -->
</script>

<style>
<!-- CSS Styles -->
</style>

<!-- HTML Markup -->
```
Next, we will edit the `App.svelte` file and replace it with the following code to complete the App component. 

```
<script>
	import Todo from './Todo.svelte'
</script>

<main>
	<Todo />
</main>
```

## Our Todo Component

For this project we will keep this clean and import the TodoItem Component into our scripts section alongside creating some variables for containing our default filter settings: todoText and nextID. Next we will create our sample todo items using the array data structure.  

```
<script>
        import TodoItem from './TodoItem.svelte';
        let newTodoText = '';
        let currentFilter = 'all';
        let nextId = 4;
        let todoList = [
        {
            id: 1,
            text: 'Write a Svelte Todo App',
            completed: true
        },
        {
            id: 2,
            text: 'Hit the ↵ button to add a new item.',
            completed: false
        },
        {
            id: 3,
            text: 'Hit this ❌ to delete an item.',
            completed: false
        }
    ];

</script>

``` 
Let's start writing the html code for our `Todo.svelte` component.

```
<div class="container-fluid">
    <h1>TODO</h1>
    <input type="text" class="todo-input" placeholder="New Item ..." bind:value={newTodoText} on:keydown={addTodo} >

    {#each filteredtodoList as todo}
        <div class="todo-item">
            <TodoItem {...todo} on:deleteTodo={handleDeleteTodo} on:toggleComplete={handleToggleComplete} />
        </div>
    {/each}

    <div class="inner-container">
        <div><label><input class="inner-container-input" type="checkbox" on:change={checkAlltodoList}>Check All</label></div>
        <div>{todoListRemaining} items left</div>
    </div>

    <div class="inner-container">
        <div>
            <button on:click={() => updateFilter('all')} class:active="{currentFilter === 'all'}">All</button>
            <button on:click={() => updateFilter('active')} class:active="{currentFilter === 'active'}">Active</button>
            <button on:click={() => updateFilter('completed')} class:active="{currentFilter === 'completed'}">Completed</button>
        </div>
        <dir>
            <button on:click={clearCompleted}>Clear Completed</button>
        </dir>
    </div>

</div>
```
Now we can implement the logic for our functions under our existing items like so. 

```
<script> 
    function addTodo(event) {
        if (event.key === 'Enter') {
            todoList = [...todoList, {
                id: nextId,
                completed: false,
                text: newTodoText
            }];
            nextId = nextId + 1;
            newTodoText = '';
        }
    }
    $: todoListRemaining = filteredtodoList.filter(todo => !todo.completed).length;
    $: filteredtodoList = currentFilter === 'all' ? todoList : currentFilter === 'completed'
        ? todoList.filter(todo => todo.completed)
        : todoList.filter(todo => !todo.completed)
    function checkAlltodoList(event) {
        todoList.forEach(todo => todo.completed = event.target.checked);
        todoList = todoList;
    }
    function updateFilter(newFilter) {
        currentFilter = newFilter;
    }
    function clearCompleted() {
        todoList = todoList.filter(todo => !todo.completed);
    }
    function handleDeleteTodo(event) {
        todoList = todoList.filter(todo => todo.id !== event.detail.id);
    }
    function handleToggleComplete(event) {
        const todoIndex = todoList.findIndex(todo => todo.id === event.detail.id);
        const updatedTodo = { ...todoList[todoIndex], completed: !todoList[todoIndex].completed};
        todoList = [
            ...todoList.slice(0, todoIndex),
            updatedTodo,
            ...todoList.slice(todoIndex+1)
        ];
    }
</script>
```

Next step is to add the following styles to complete our `Todo.Svelte` file. 

```
<style>
    h1 {
	color: #ff3e00;
	text-transform: uppercase;
	font-size: 4em;
	font-weight: 100;
	}
    .container-fluid{
        max-width: 800px;
        margin: 10px auto;
    }
    .todo-input {
        width: 100%;
        padding: 10px, 20px;
        font-size: 18px;
        margin-bottom: 20px;
    }
    .inner-container {
        display: flex;
        align-items: center;
        justify-content: space-between;
        font-size: 16px;
        border-top: 1px solid lightgrey;
        padding-top: 15px;
        margin-bottom: 13px;
    }
    .inner-container-input {
        margin-right: 12px;
    }
    button {
        font-size: 14px;
        background-color: white;
        appearance: none;
        border: none;
    }
    button:hover {
        background: #ff3e00;
        color: white;
    }
    button:focus {
        outline: none;
    }
    .active {
        background: #ff3e00;
        color: white;
    }
</style>
```

## Our TodoItem Component

Let's start by importing  createEventDispatcher to be able to create and emit custom events that occur within our Todo App. Next step is to give access to fly from svelte/transitions by adding the following code. 

```
<script> 
    import { createEventDispatcher } from 'svelte';
    import { fly } from 'svelte/transition';
</script>
```
We now need to make sure that our TodoItem Component is able to recieve the parameters we defined in Todo.Svelte. 

```
<script>
    import { createEventDispatcher } from 'svelte';
    import { fly } from 'svelte/transition';
    export let id;
    export let text;
    export let completed;

</script>
```
Next step is to create our dispatch instance and add the logic for the deleteTodo and toggleCompleted Function.

```
<script>
    import { createEventDispatcher } from 'svelte';
    import { fly } from 'svelte/transition';
    export let id;
    export let text;
    export let completed;
    const dispatch = createEventDispatcher();
    function deleteTodo() {
        dispatch('deleteTodo', {
            id: id
        });
    }
    function toggleCompleted() {
        dispatch('toggleCompleted', {
            id: id
        });
    }
</script>
```
We now have to create the html logic for the TodoItem.Svelte component. 

```
<div class="todo-item">
    <div class="todo-item-left" transition:fly="{{ y: 20, duration: 300}}">
        <input type = "checkbox" bind:checked={completed} on:change={toggleCompleted}>
        <div class="todo-item-label" class:completed={completed}>{text}</div>
    </div>
    <div class="remove-item" on:click={deleteTodo}>
    ❌
    </div>
</div>
```
Note 💡 - Even without the css you should now be able to view the sample list data from our Todo.svelte file and add your own items to the Svelte TodoApp.

Cool! We can now implement our styles to our TodoItem component  and finish off our project. 

```
<style>
    .todo-item {
        margin-bottom: 15px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        animation-duration: 0.3s;
    }
    .remove-item {
        cursor: pointer;
        margin-left: 15px;
    }
    .remove-item:hover {
        color: lightseagreen; 
    }
    .todo-item-left {
        display: flex;
        align-items: center;
    }
    .todo-item-label {
        border: 1px solid white;
        margin-left: 12px;
    }
    .completed {
        text-decoration: line-through;
        color: grey;
        opacity: 0.4;
    }
</style>
```
## Recap

If you followed along then you should have completed the project and finished off your Svelte Todo App.

Now if you made it this far, then I am linking the code to my [Sandbox](https://e8suno.csb.app/) for you to fork or clone and then the job's done.

## License: 📝
This project is under the MIT License (MIT). See the LICENSE for more information.

## Contributions
Contributions are always welcome...

- Fork the repository
- Improve current program by
- improving functionality
- adding a new feature
- bug fixes
- Push your work and Create a Pull Request

## Useful Resources

- https://sveltestrap.js.org/
- https://svelte.dev/
- https://github.com/sveltejs/template
- https://svelte.dev/repl/7eb8c1dd6cac414792b0edb53521ab49?version=3.20.1
- https://svelte.dev/repl/4162507931ea406c9f03be1b3aed9e94?version=3.48.0