Skip to content

CRUD Operations

Adding new tasks

Now that we can see the list of tasks, it's time to add a few more. We create a form which executes the addTask function that invokes taskRepo.insert(). Update your +page.svelte as follows:

svelte

<script lang="ts">
  import { repo } from "remult";
  import { Task } from "../shared/Task";

  let tasks = $state<Task[]>([]);

  $effect(() => {
    repo(Task)
      .find()
      .then((t) => (tasks = t));
  });

  let newTaskTitle = $state(""); 
  const addTask = async (event: Event) => {
    event.preventDefault();
    const newTask = await repo(Task).insert({ title: newTaskTitle });
    tasks = [...tasks, newTask];
    newTaskTitle = "";
  };
</script>

<div>
  <h1>todos</h1>
  <main>
    <form onsubmit={addTask}>
      <input bind:value={newTaskTitle} placeholder="What needs to be done?" />
      <button>Add</button>
    </form>

    {#each tasks as task}
      <div>
        <input type="checkbox" bind:checked={task.completed} />
        <span>{task.title}</span>
      </div>
    {/each}
  </main>
</div>

The call to insert will make a post request to the server, insert the new task to the db, and return the new Task object with all it's info (including the id generated by the database)

Try adding a few tasks to see how it works.

Mark Tasks as Completed

  1. Add a setCompleted function in the script section as follows:
ts
const setCompleted = async (task: Task, completed: boolean) => {
  await repo(Task).save({ ...task, completed })
}
  1. Modify the checkbox to invoke the method:
svelte
<div>
	<input
		type="checkbox"
		checked={task.completed}
		oninput={(e) => setCompleted(task, e.currentTarget.checked)}
	/>
	<span>{task.title}</span>
</div>

Rename Tasks

To make the tasks in the list updatable, we'll use an input element and bind it to the task's title property. We'll also add a Save button to commit the changes to the backend database.

  1. Add a saveTask function in the script section as follows:
ts
const saveTask = async (e: Event, task: Task) => {
  e.preventDefault()
  await repo(Task).save({ ...task })
}
  1. Update the html part
svelte
{#each tasks as task}
	<div>
		<input
			type="checkbox"
			checked={task.completed}
			oninput={(e) => setCompleted(task, e.currentTarget.checked)}
		/>
		<input name="title" bind:value={task.title} />
		<button onclick={(e) => saveTask(e, task)}>Save</button>
	</div>
{/each}

The saveTask function saves the task that is passed in. Since the task's title is bound to the input, changes are made directly to the task.

Make some changes and refresh the browser to verify that the backend database is updated.

Browser's Network tab

As you play with these CRUD capabilities, monitor the network tab and see that they are all translated to rest api calls.

Delete Tasks

Let's add a Delete button next to the Save button of each task in the list.

  1. Add the deleteTask function
ts
const deleteTask = async (e: Event, task: Task) => {
  e.preventDefault()
  await repo(Task).delete(task)
  tasks = tasks.filter((c) => c.id !== task.id)
}
  1. Add the Delete button
svelte
{#each tasks as task}
	<div>
		<input
			type="checkbox"
			checked={task.completed}
			oninput={(e) => setCompleted(task, e.currentTarget.checked)}
		/>
		<input name="title" bind:value={task.title} />
		<button onclick={(e) => saveTask(e, task)}>Save</button>
		<button onclick={(e) => deleteTask(e, task)}>Delete</button>
	</div>
{/each}

MIT Licensed | Made by the Remult team with ❤️