Frontend - Svelte
Prerequisites
- deployed todos api
- nodejs
Setup
We will be using the Snowpack/Svelte Template, Snowpack is a new bundler that takes advantage of ES Modules for a smoother hot reload experience as your app gets bigger
- generate a new project
npx merced-spinup@latest snowsvelte todofrontend
- cd into new todofrontend folder
- run
npm install
- run dev server with npm start
- install some extra dependencies
npm i svelte-routing milligram
- import milligram in index.js to have some default styling
import App from "./App.svelte";
import "milligram";
let app = new App({
target: document.body,
});
export default app;
// Hot Module Replacement (HMR) - Remove this snippet to remove HMR.
// Learn more: https://www.snowpack.dev/concepts/hot-module-replacement
if (import.meta.hot) {
import.meta.hot.accept();
import.meta.hot.dispose(() => {
app.$destroy();
});
}
Create Our Components
- create a pages folder in src and create the follow
AllPosts.svelte
<div>
<h1>All Posts</h1>
</div>
<style></style>
<script></script>
SinglePost.svelte
<div></div>
<style></style>
<script></script>
Form.svelte
<div></div>
<style></style>
<script></script>
App.svelte
Let's do the following
- setup our routes
- grab the list of todos when App mounts
App.svelte
<script>
import { onMount } from "svelte";
import { Router, Route, Link } from "svelte-routing";
import AllPosts from "./pages/AllPosts.svelte";
import SinglePost from "./pages/SinglePost.svelte";
import Form from "./pages/Form.svelte";
export let url = ""; // prop for router to url
let todos; // variable to hold todo
let baseURL = "https://api.herokuapp.com/todos/"; //api url
//function to get todos
const getTodos = async () => {
const response = await fetch(baseURL);
const data = await response.json();
todos = data;
};
onMount(() => getTodos());
</script>
<style>
.app {
text-align: center;
}
</style>
<Router url="{url}">
<div class="app">
<h1>Our Todos</h1>
<main>
<Route path="/post/:id" let:params
><SinglePost
todos="{todos}"
id="{params.id}"
getTodos="{getTodos}"
url="{baseURL}"
/></Route>
<Route path="/new"
><form todos="{todos}" url="{baseURL}" getTodos="{getTodos}"
/></Route>
<Route path="/edit/:id" let:params
><form
todos="{todos}"
id="{params.id}"
url="{baseURL}"
getTodos="{getTodos}"
/></Route>
<Route path="/"><AllPosts todos="{todos}" /></Route>
</main>
</div>
</Router>
AllPosts.svelte
In this component we will...
- receive the todos prop
- Loop through the todos and render them
- Link to the SinglePost route
<div>
{#each todos as todo, index}
<article>
<Link to="{'/post/' + todo.id}"><h1>{todo.subject}</h1></Link>
<h2>{todo.details}</h2>
</article>
{/each}
</div>
<style>
</style>
<script>
import {Link} from "svelte-routing"
export let todos = []; //receive todos props
</script>
SinglePost.svelte
In this post we will...
- we receive the todos array and id and as props
- we will find the post to display
- display the post
- add a button to link to the main page
<div>
<h1>{post.subject}</h1>
<h2>{post.details}</h2>
<Link to="/"><button>Back to Main</button></Link>
</div>
<style>
</style>
<script>
import {Link} from "svelte-routing"
// gets posts and id props
export let todos;
export let id;
export let getTodos; // receive getTodos as props
export let url; // get url as prop
const post = todos.find(p => p.id == id)
</script>
Form.svelte
We will be using this form for editing and creating
- receive the todos array, url and id as props
- if id is defined, we will set for editing, if not it'll default to creating
<div>
<form on:submit="{handleSubmit}">
<input type="text" placeholder="subject" bind:value="{subject}" />
<input type="text" placeholder="details" bind:value="{details}" />
<input type="submit" bind:value="{buttonLabel}" />
</form>
</div>
<style></style>
<script>
import { navigate } from "svelte-routing";
export let todos; // receive todos array prop
export let id; // receive id prop
export let url; // receive url prop
export let getTodos; // receive getTodos prop
let buttonLabel = "Create Todo"; //default for submit button text
let subject = ""; //default for subject field
let details = ""; //default for subject field
// default value of handle submit for creating todos
let handleSubmit = async (event) => {
event.preventDefault();
await fetch(url, {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
subject,
details,
}),
});
getTodos();
navigate("/", { replace: true });
};
// if id is defined, reconfigure for editing
if (id) {
buttonLabel = "edit a todo";
const post = todos.find((p) => p.id == id);
subject = post.subject;
details = post.details;
handleSubmit = async (event) => {
event.preventDefault();
await fetch(url + id + "/", {
method: "put",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
id,
subject,
details,
}),
});
getTodos();
navigate("/", { replace: true });
};
}
</script>
In App.svelte add a Create Todo Button
<Router url="{url}">
<div class="app">
<h1>Our Todos</h1>
<Link to="/new"><button>Make New Todo</button></Link>
<main>
<Route path="/post/:id" let:params><SinglePost todos={todos} id={params.id}/></Route>
<Route path="/new"><Form todos={todos} url={baseURL} getTodos={getTodos}/></Route>
<Route path="/edit/:id" let:params><Form todos={todos} id={params.id} url={baseURL} getTodos={getTodos}/></Route>
<Route path="/"><AllPosts todos={todos}/></Route>
</main>
</div>
</Router>
in SinglePost.svelte let's add a Edit a Todo Button!
<div>
<h1>{post.subject}</h1>
<h2>{post.details}</h2>
<Link to="/"><button>Back to Main</button></Link>
<Link to={'/edit/' + post.id}><button>Edit the Todo</button></Link>
</div>
Last, delete button in SinglePost.svelte
<div>
<h1>{post.subject}</h1>
<h2>{post.details}</h2>
<Link to="/"><button>Back to Main</button></Link>
<Link to={'/edit/' + post.id}><button>Edit the Todo</button></Link>
<button on:click={deleteTodo}>Delete Todo</button>
</div>
<style>
</style>
<script>
import {Link, navigate} from "svelte-routing"
// gets posts and id props
export let todos;
export let id;
export let url;
export let getTodos;
const post = todos.find(p => p.id == id)
// delete todo function
const deleteTodo = async () => {
await fetch(url + id + "/", {
method: "delete",
})
getTodos()
navigate("/", {replace: true})
}
</script>