Svelte is Great, How I build forms on it with a store for state management 🔥
Tags: svelte, development, forms, store
Published: 2020-05-13 08:36:20


About a week ago I went out to the wild discovering another frontend framework to try and learn, and I end up founding **Svelte**, actually, I already have tried but not that in dept. Have been using it to build a side project, made me learn a lot about it. This blog post I will share with you a bit about and how I am using svelte to create forms.

![svelte-logo](https://nunomalex.me/svelte-logo.png "svelte logo")

# What is svelte? 

Svelte is a framework/compiler with an abstraction logic that updates the DOM making it fast and simple. Let me show you an example, of how simple it is:

```html
<script>
  let name = 'Nuno';
</script>

<p>My name is {name}</p>
```
(App.svelte)

*What you see in this example seams like HTML right ?* That is true the svelte syntax is very similar to HTML but with some cool reactivity effects.

In this example, we can see a `script` tag that holds the `name` variable and on the `<p>` block we are referencing the `name`, so the result will be:
```text
My name is Nuno
```

So as you can see svelte files holds javascript and HTML at the same time, like VUE, but with a different syntax.

Another cool thing is that you can even declare styling on the svelte file also, like so:
```html
<style>
  p { color: red; }
</style>
```

### *Basic stuff right ?*

There is a lot of the syntax to learn but if you want to learn them would be better to check out the svelte documentation, [here](https://svelte.dev/docs). Although in the following sections, I will show you how to use svelte with forms, in this way you will learn more in dept about *svelte* and at the same time how to create reusable form and components.

# Lets create a Form component

## Creation page
```html
<script>
  import { saving, save, errors, success } from "../stores/product.js";
  import Form from './ProductForm.svelte';
  
  let form = {
    name: '',
    price: 0
  };

  const handleSubmit = () => {
    save(form);
  };
</script>
  
<Form bind:form={form} on:submit={handleSubmit} errors={$errors} loading={$saving} />
```
(create-product.svelte)

This code holds the product creation page, and as you can see we have a script tag importing some values and methods from the product store (I will get on that in a minute), as well a Product Form component (that can be reused on the creation of the product as well as on the product editing page). 

In the HTML part, you can see that we are passing the form object into the Form component with `bind:form={form}`, this will bind the form object, meaning that when the form object updates inside the Form component, it will be reflected on the creation page as well.

The `on:submit={handleSubmit}` is an event listener, the will trigger whenever the `Form` fires/dispatchs `submit` event.

The `errors={$errors} loading={$saving}` props are values that we receive from the store and are passed to our `Form` component (the `$` is just a shortcut to subscribe to the store, I will explain more in dept bellow).

## Form component

```html
<script>
  import { createEventDispatcher } from 'svelte';
  import Field from "../components/Field.svelte";

  const dispatch = createEventDispatcher();
  
  export let form; // prop
  export let errors; // prop
  export let loading; // prop
</script>
<form on:submit|preventDefault={() => dispatch('submit')}>
  <Field
    name="name"
    label="Name"
    bind:value={form.name}
    errors={errors} />
  <Field 
    name="price" 
    label="Price" 
    bind:value={form.price} 
    errors={errors} />
    <div class="mt-4 flex justify-between">
      <button
        type="submit"
        class={loading ? "button loading" : "button success"}
        disabled={loading}>
        Save
      </button>
    </div>
</form>
```
(ProductForm.svelte)

The Product Form looks fairly simple, in the `script` tag, we are importing the `Field` component, to use it as our inputs but already abstracted with the markup and necessary logic to work. Then we are importing `createEventDispatcher` that will provide us with a `dispatch` letting us communicate with a parent component. To receive the props from a parent component, we are exporting some variables (form, errors, loading), using `export` is required, by using it we are tailing the parent and svelte that we want to receive that as props.

In the HTML part, we are defining a form with an `on:submit` event and passing a preventDefault modifier (allows us to prevent the default browser behavior when the event fires), in fact, we can add as many modifiers as we want, see more [here](https://svelte.dev/docs#on_element_event), as a value, we are passing a function that will dispatch the `submit` event. If you remember on the Product creation form we are using `on:submit={handleSubmit}` to listen to for the `submit` event from the child. So by dispatching events from the children to the parent, we are able to communicate between them.

We are also declaring some `Field` components that will receive the value that will be bound, and also the errors prop, that will be used to display errors on the input itself.

To finish the Form component we have a submit button which has the `class` props, so when the user `[prop]={ ...expression }`, the value of the `prop` will be always the result of the expression. 

## Field Component

```html
<script>
  export let value;
  export let label;
  export let name;
  export let errors = {};

  $: hasErrors = errors && errors[name];
  $: classes = hasErrors ? "input with-error" : "input";

  const handleInput = e => {
    value = e.target.value;
  };
</script>

<div class="mb-4">
  <input
    type="text"
    placeholder={label}
    {name}
    class={classes}
    bind:value
    on:input={handleInput} />
    {#if hasErrors}
      <p class="text-red-500 text-xs italic mt-3">{errors[name].message}</p>
    {/if}
</div>
```

In the `Field` component we have some props as expected, two *computed* variables, when we have `$: [varName] = {expression}` will create a variable that will recompute when the component updates (like a computed method in *vue*). Then we are creating a method to handle the input change, this means that `on:input={handleInput}` will listen to any change on the input and when it happens it will trigger the `handleInput` method, that therefore will update the value, since the value is being bound with the parent value will be updated as well.

For the first time we can see an `if` statement, as there are others (see [here](https://svelte.dev/docs#each))

# The Store

```js
import { writable } from 'svelte/store';
import * as api from 'api.js';

let errors = writable(null);
let saving = writable(false);
let success = writable(null);

const save = async (form) => {
  saving.set(true);
  errors.set(null);

  const response = await api.post('product', form);
  
  if (response.status >= 400) {
    saving.set(false);
    errors.set(response.data.errors);
    return;
  }

  success.set(response.data.message);
  order.set(response.data.data);

  saving.set(false);
};

export default { errors, saving, success, save}
```
(product.js)

To abstract logic from components we often need to create a store, this can also be useful when we need to share information across multiple components. *Svelte* has a couple of functions that help us create stores, like `writable`, `readable`, `derived`. When accessing store variables on a svelte component we normally use `$` prefix, which will set the appropriate store subscription with that component, making us access it without any more logic. 

The `writable` returns a object that has additional `set` and `update` methods, more about [here](https://svelte.dev/docs#writable).

Our store has multiple `writable`(s) like `errors, success, saving` that will be used later on our components with `$` prefix, to be subscribed. We are also declaring a `save` method that is responsible to send our product to the API and set the `errors` or `success` accordingly.

# Conclusion

There is a lot more about *Svelte* than what I have shown you, although I think this will be a good start. If you have any suggestion feel free to reach out to me.

![easy](https://media.giphy.com/media/3o7btNa0RUYa5E7iiQ/giphy.gif)

You can see also the blog post on my blog [here](https://nunomalex.me/blog/svelte-is-great-how-i-build-forms-on-it).

**Disclaimer:** Writing blog posts is a big challenge for me if you think that something is wrong, feel free to reach out to me.