Categories
Svelte

Handling Inputs with Svelte

Svelte is an up and coming front end framework for developing front end web apps.

It’s simple to use and lets us create results fast.

In this article, we’ll look at how to handle text inputs in Svelte components.

Data Bindings

We can bind input values to variables in our components with the bind:value directive.

To use it we can write the following:

App.svelte :

<script>
  let name = "";
</script>

<input bind:value={name}>

<p>{name}</p>

In the code above, we used the bind:value directive to bind the input’s value to the name variable in our component.

Therefore, when we type into the input, the value will be shown in the p element below the input.

In the DOM, everything is a string. We have to convert the inputted values to the value that we want before using it.

bind:value takes care of that.

For example, if we write:

<script>
  let a = 1;
</script>

<label>
  <input type=number bind:value={a} min=0 max=10>
</label>
<br>
<label>
  <input type=range bind:value={a} min=0 max=10>
</label>

Then we’ll see both inputs change if we type in a value to the number input or change the slider value.

Checkbox Inputs

Svelte can handle checkbox inputs. So we can get the checked values from checkboxes easily.

For example, we can write the following code:

<script>
  let yes = false;
</script>

<input type=checkbox bind:checked={yes}>
<p>{yes}</p>

and we’ll see the value in the p tag change as we check and uncheck the checkbox since we have the bind:checked attribute to bind the checked value to the yes variable.

Group Inputs

We can use the bind:group directive to bind the values of the group to one value.

For instance, this is useful for radio buttons since a group of radio buttons shares one value. Only one can be selected at a time.

To make a radio button group with bind:group , we can write the following:

<script>
 let fruits = ["apple", "grape", "orange"];
 let selectedFruit = "";
</script>

{#each fruits as fruit}
<label>
  <input type=radio bind:group={selectedFruit} value={fruit} >
  {fruit}
  <br>
</label>
{/each}
<p>{selectedFruit}</p>

In the code above, we have bind:group to bind to the selectedFruit , which has the selected value of the group.

We set the value attribute of each radio button to fruit so that when we click it, Svelte will set the value of selectedFruit to the value that we set to value .

Therefore, when we click a radio button, we’ll see the selected value of the radio button group in the p element.

Also, only one radio button is selected at one time.

Textarea Inputs

Text area inputs work like text inputs. For instance, we can write:

<script>
 let text = "";
</script>

<textarea bind:value={text}></textarea>
<p>{text}</p>

bind:value bind the value entered in the textarea to the value text .

Then we get text displayed in the p tag.

Select Bindings

We can bind value from select elements by writing the following code:

<script>
  let selected = "";
  let names = [
    { id: 1, name: "Jane" },
    { id: 2, name: "John" },
    { id: 3, name: "Mary" }
  ];
</script>

<select bind:value={selected} >
{#each names as name}
  <option value={name}>
    {name.name}
  </option>
{/each}
</select>
<p>{selected.name}</p>

In the code above, we passed in the whole name object into the value prop.

Then when we select an option, we’ll see selected set to the selected choice and we can display the name property to display the name.

Multiple Selection

We can select multiple items from select elements with the multiple attribute set.

For instance, we can write the following code:

<script>
   let selected = "";
   let fruits = ["apple", "orange", "grape"];
</script>

<select bind:value={selected} multiple >
{#each fruits as fruit}
  <option value={fruit}>
    {fruit}
  </option>
{/each}
</select>
<p>{selected}</p>

All we had to do is pass selected to the bind:value directive, then Svelte will automatically get all the selected values.

Therefore, when we choose one or more values from the list, we’ll see them displayed in the p element.

Contenteditable Bindings

Any element with contenteditable set to true can bind to variables in our code.

For instance, we can write:

<script>
   let html = "";
</script>

<style>
      [contenteditable] {
        height: 200px;
        width: 90vw;
      }
</style>

<div
  contenteditable="true"
  bind:innerHTML={html}
></div>
<p>{html}</p>

In the code above, we set our div’s contenteditable attribute to true . Then we use bind:innerHTML to bind to the html variable.

Since we have contenteditable set to true , we can type into it, and have the inputted value displayed in the p tag since bind does the data binding.

Each Bindings

Binding to elements in the each block will mutate the values of the entry that’s being modified.

For instance, if we have:

<script>
  let todos = [
    { done: false, text: "eat" },
    { done: false, text: "drink" },
    { done: false, text: "sleep" }
  ];

$: remaining = todos.filter(t => !t.done).length;
</script>

<h1>Todos</h1>

{#each todos as todo}
  <div class:done={todo.done}>
    <input
      type=checkbox
      bind:checked={todo.done}
    >

    <input
      placeholder="What needs to be done?"
      bind:value={todo.text}
    >
  </div>
{/each}

<p>{remaining} remaining</p>

Then when we change the value of the input or the checkbox, they’ll be reflected in the template.

For instance, remaining will be updated as we check and uncheck the checkboxes.

Conclusion

We can bind our inputted values from various controls to variables’ values with the bind directive.

It works with any native form element in addition to anything that has the contenteditable attribute set to true .

Categories
Svelte

Handling Component and DOM Events in Svelte Apps

Svelte is an up and coming front end framework for developing front end web apps.

It’s simple to use and lets us create results fast.

In this article, we’ll look at how to handle DOM events in Svelte components.

Events

We can use the on: directive to handle events. For instance, we can write the following to get the mouse position as we move our mouse around the screen:

App.svelte :

<script>
  let pos = { x: 0, y: 0 };

  const handleMousemove = event => {
    pos.x = event.clientX;
    pos.y = event.clientY;
  };
</script>

<style>
  div {
    width: 100vw;
    height: 100vh;
  }
</style>

<div on:mousemove={handleMousemove}>
  The mouse position is {pos.x} x {pos.y}
</div>

In the code above, we have on:mousemove={handleMousemove} to attach the handleMousemove handler to our div.

The handleMousemove function has the event parameter, which is the MouseEvent object.

Therefore, we can access the mouse position with the clientX and clientY properties for the x and y position of the mouse respectively.

In the end, we display them on the screen.

Event handlers can also be declared inline as follows:

App.svelte :

<script>
  let pos = { x: 0, y: 0 };
</script>

<style>
  div {
    width: 100vw;
    height: 100vh;
  }
</style>

<div on:mousemove={event => {
    pos.x = event.clientX;
    pos.y = event.clientY;
  }}>
  The mouse position is {pos.x} x {pos.y}
</div>

Or we can put them between quotes as follows:

App.svelte :

<script>
  let pos = { x: 0, y: 0 };
</script>

<style>
  div {
    width: 100vw;
    height: 100vh;
  }
</style>

<div on:mousemove="{event => {
    pos.x = event.clientX;
    pos.y = event.clientY;
  }}">
  The mouse position is {pos.x} x {pos.y}
</div>

The quotes provide syntax highlighting in some environments, but they’re optional.

Inline event handler doesn’t impact the performance of the app since Svelte will optimize it by not attaching and detaching on the fly.

Modifiers

DOM event directives can have modifiers attached to them to change their behavior.

For instance, we can add the once modifier to the on:click directive to only listen to mouse click of an element once:

App.svelte :

<script>
  const handleClick = () => {
    alert("alert");
  };
</script>

<button on:click|once={handleClick}>
  Click Me Once
</button>

The once modifier in the code above only runs the handleClick handler once, so if we click the button again, it’ll run nothing.

Other modifiers include:

  • preventDefault — calls event.preventDefault before running the handler
  • stopPropagation — calls event.stopPropagation to prevent the event from reaching the next element
  • passive — improves scrolling performance on touch or wheel events
  • capture — fires handlers during the capture phase instead of the bubbling phase
  • self — only trigger handler if event.target is the element itself.

They can also be chained together like on:click|once|self .

Photo by Marvin Meyer on Unsplash

Component Events

Components can also dispatch events. For instance, we can write the following code to dispatch an event from a child to a parent:

App.svelte :

<script>
  import Button from "./Button.svelte";
  const handleGreet = event => {
    alert(event.detail.text);
  };
</script>

<Button on:greet={handleGreet} />

Button.svelte :

<script>
  import { createEventDispatcher } from "svelte";

  const dispatch = createEventDispatcher();

  const greet = () => {
    dispatch("greet", {
      text: "Hello Mary"
    });
  };
</script>

<button on:click={greet}>
  Send Greeting
</button>

In Button, we create the dispatch constant with the createEventDispatcher function. dispatch is a function, which we call with the event name and the payload to send with the event emission as the arguments.

Then in App , we listen to the greet event emitted from Button by attaching the handleGreet event listener to the on:greet directive.

When handleGreet runs after the greet event is emitted, then the event object has the data sent with the event emission and we retrieved it with event.detail.text .

Therefore, when we click the Send Greeting button, we see ‘Hello Mary’ displayed in an alert box.

Event Forwarding

Component events don’t bubble like DOM events. To forward them to another component, we can add a forward function, which calls dispatch .

Then we can listen to the event in the component where the event is forwarded to.

For instance, we can write:

Button.svelte :

<script>
  import { createEventDispatcher } from "svelte";

  const dispatch = createEventDispatcher();

  const greet = () => {
    dispatch("greet", {
      text: "Hello Mary"
    });
  };
</script>

<button on:click={greet}>
  Send Greeting
</button>

Parent.svelte :

<script>
  import Button from "./Button.svelte";
  import { createEventDispatcher } from "svelte";

  const dispatch = createEventDispatcher();

  const forward = () => {
    dispatch("greet", event.detail);
  };
</script>

<Button on:greet />

App.svelte :

<script>
  import Parent from "./Parent.svelte";
  const handleGreet = event => {
    alert(event.detail.text);
  };
</script>

<Parent on:greet={handleGreet} />

In the code above, Button.svelte emits the greet event, then the Parent.svelte component forwards the event, then App.svelte listens to the greet event.

In Parent.svelte ,

<Button on:greet />

is the same as:

<Button on:greet={forward} />

DOM Event Forwarding

We can forward DOM events like component events. For instance, we can write:

Button.svelte :

<button on:click>
  Send Greeting
</button>

Parent.svelte :

<script>
  import Button from "./Button.svelte";
</script>

<Button on:click />

App.svelte :

<script>
  import Parent from "./Parent.svelte";
  const handleClick = event => {
    alert("Hello");
  };
</script>

<Parent on:click={handleClick} />

The code above forwards the click event from Buttonto Parent and Parent to App .

Therefore, we’ll see a ‘Hello’ alert displayed on the screen when we click the Send Greeting button.

Conclusion

Svelte components and DOM elements can have event handlers attached to them.

We listen to events using the on: directives with modifiers.

Events can be forwarded with the on: directive without any value set to it.

Categories
Svelte

Composing Svelte Components with Slots

Svelte is an up and coming front end framework for developing front end web apps.

It’s simple to use and lets us create results fast.

In this article, we’ll look at how to nest Svelte components with slots.

Component Composition

We can add the slot component to a Svelte component to let us nest components inside another component.

For example, we can use the slot component as follows:

App.svelte :

<script>
import Parent from "./Parent.svelte";
import Foo from "./Foo.svelte";
</script>

<Parent>
  <Foo />
</Parent>

Parent.svelte :

<div>
  <p>Start</p>
  <p>
    <slot></slot>
  </p>
  <p>End</p>
</div>

Foo.svelte :

<button>
  foo
</button>

In the code above, we have the Parent component, which has the slot component, which lets us nest one or more components inside it.

Parent has 3 p elements. The slot is in the 2nd p element.

We created the Foo component with a button. Then when we write:

<Parent>
  <Foo />
</Parent>

in App.svelte , we’ll see the button displayed between ‘start’ and ‘end’.

Fallback Content

We can put fallback content in between the slot tags so that when we didn’t pass in anything in between the tags of the component with the slot component, then we’ll still see something displayed.

For example, we can write the following code to add fallback content and display it:

App.svelte :

<script>
import Parent from "./Parent.svelte";
</script>

<Parent />

Parent.svelte :

<div>
  <p>Start</p>
  <p>
    <slot>
      <p>Nothing to see here</p>
    </slot>
  </p>
  <p>End</p>
</div>

In the code above, we have:

<p>Nothing to see here</p>

in between the slot tags. Then when we write <Parent /> as we did in App.svelte , we’ll see:

Start

Nothing to see here

End

displayed on the screen since we used a self-closing version of Parent instead of passing child elements in between the tags.

Multiple Slots with Named Slots

We can add multiple slots by naming the slots. Then we can add content for each slot with the slot attribute with the name of the slot as the value.

For instance, we can write the following code to add multiple slots and populate them with content:

Contact.svelte :

<article>
  <h2>
    <slot name="name">
      <span>No name</span>
    </slot>
  </h2>

  <div class="address">
    <slot name="address">
      <span>No address</span>
    </slot>
  </div>

  <div class="email">
    <slot name="email">
      <span>No email</span>
    </slot>
  </div>
</article>

App.svelte

<script>
import Contact from "./Contact.svelte";
</script>

<Contact>
  <span slot="name">
    Jane Smith
  </span>

  <span slot="address">
    123 A St.
  </span>

  <span slot="email">
    abc@abc.com
  </span>
</Contact>

In the code above, we defined multiple slots with the name attribute added to each in Contact.svelte .

Then we can use the Contact component in App.svelte when we pass in components into each slot as identified by their names. Therefore, we should see:

Jane Smith

123 A St.
abc@abc.com

displayed on the screen.

Passing Child Data to Parent Component via Slots

We can use slot props to pass data from the child component to the parent component. To define slot props, we set the prop name with the variable that we want to pass to the parent as the value.

To define slot props and use it, we can write the following code:

Contact.svelte :

<script>
let names = ["John", "Jane", "Mary"];
</script>

<article>
  <slot names={names}></slot>
</article>

The code above has the slot prop names , which we pass the names variable into it.

Then we can use it in App.svelte as follows:

<script>
import Contact from "./Contact.svelte";
</script>

<Contact let:names={names}>
  {#each names as name}
    <p>{name}</p>
  {/each}
</Contact>

The let directive defines the slot prop variable, which should have the slot prop as the value.

In the code above, let:names={names} sets the slot props with the names prop from Contact as the value for the names variable in App.svelte .

Then we can access names inside the loop as we did in the each loop.

Therefore, we should get:

John

Jane

Mary

displayed on the screen.

Named slots can also have props. We can write:

Contact.svelte :

<script>
let contactName = "Jane Smith";
let address = "123 A. St";
let email = "abc@abc.com";
</script>

<article>
  <h2>
    <slot name="name" contactName={contactName}>
      <span>No name</span>
    </slot>
  </h2>
  <div class="address">
    <slot name="address" address={address}>
      <span>No address</span>
    </slot>
  </div>
  <div class="email">
    <slot name="email" email={email}>
      <span>No email</span>
    </slot>
  </div>
</article>

App.svelte :

<script>
import Contact from "./Contact.svelte";
</script>

<Contact>
  <span slot="name" let:contactName={contactName}>
    {contactName}
  </span>
  <span slot="address" let:address={address}>
    {address}
  </span>
  <span slot="email" let:email={email}>
    {email}
  </span>
</Contact>

In the code above, we defined slot props for each slot, and then pass them into the App.svelte component with the let directive as we did with the single slot example.

Therefore, we should have:

Jane Smith

123 A St.
abc@abc.com

displayed as we did with the earlier example.

Conclusion

We can use slots to nest components within another component. To pass data from the child components to the parent, we can use slot props.

Slot props are defined with the let directive. The value of it should be the variable name to reference the slot prop value. Then we can access the slot prop value in between the tags.

Fallback content can be added between the slot tags and they’re displayed and we reference a component with a self-closing tag.

Categories
Vuetify

Vuetify — Weather Card and Carousel

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Weather Card

We can add a weather card with Vuetify.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-card class="mx-auto" max-width="400">
          <v-list-item two-line>
            <v-list-item-content>
              <v-list-item-title class="headline">London</v-list-item-title>
              <v-list-item-subtitle>Mon, 12:30 PM, Mostly sunny</v-list-item-subtitle>
            </v-list-item-content>
          </v-list-item>

          <v-card-text>
            <v-row align="center">
              <v-col class="display-3" cols="6">23&deg;C</v-col>
              <v-col cols="6">
                <v-img
                   src="https://cdn.vuetifyjs.com/images/cards/sun.png"
                  alt="Sunny image"
                  width="92"
                ></v-img>
              </v-col>
            </v-row>
          </v-card-text>

          <v-list-item>
            <v-list-item-icon>
              <v-icon>mdi-send</v-icon>
            </v-list-item-icon>
            <v-list-item-subtitle>23 km/h</v-list-item-subtitle>
          </v-list-item>

          <v-list-item>
            <v-list-item-icon>
              <v-icon>mdi-cloud-download</v-icon>
            </v-list-item-icon>
            <v-list-item-subtitle>48%</v-list-item-subtitle>
          </v-list-item>

          <v-slider v-model="time" :max="6" :tick-labels="labels" class="mx-4" ticks></v-slider>

          <v-list class="transparent">
            <v-list-item v-for="item in forecast" :key="item.day">
              <v-list-item-title>{{ item.day }}</v-list-item-title>

              <v-list-item-icon>
                <v-icon>{{ item.icon }}</v-icon>
              </v-list-item-icon>

              <v-list-item-subtitle class="text-right">{{ item.temp }}</v-list-item-subtitle>
            </v-list-item>
          </v-list>

          <v-divider></v-divider>

          <v-card-actions>
            <v-btn text>Full Report</v-btn>
          </v-card-actions>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    labels: ["SU", "MO", "TU", "WED", "TH", "FR", "SA"],
    time: 0,
    forecast: [
      {
        day: "Tuesday",
        icon: "mdi-white-balance-sunny",
        temp: "24xB0/12xB0",
      },
      {
        day: "Wednesday",
        icon: "mdi-white-balance-sunny",
        temp: "22xB0/14xB0",
      },
      { day: "Thursday", icon: "mdi-cloud", temp: "25xB0/15xB0" },
    ],
  }),
};
</script>

We add a v-card with the v-list-item with the text.

The v-card-text has the degrees and image for the weather.

Below that, we have the text for the cloud and wind speed.

And we have the slider for the days.

And a list for the forecast.

The forecast are rendered from the forecast object.

At the bottom, we have the v-btn for the actions.

Carousels

The v-carousel component is used to display a large number of visual content on a rotating timer.

We can create a simple one by writing:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-carousel cycle height="400" hide-delimiter-background show-arrows-on-hover>
          <v-carousel-item v-for="(slide, i) in slides" :key="i">
            <v-sheet :color="colors[i]" height="100%">
              <v-row class="fill-height" align="center" justify="center">
                <div class="display-3">Slide {{ slide }}</div>
              </v-row>
            </v-sheet>
          </v-carousel-item>
        </v-carousel>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    colors: [
      "indigo",
      "warning",
      "pink darken-2",
      "red lighten-1",
      "deep-purple accent-4",
    ],
    slides: [1, 2, 3, 4, 5],
  }),
};
</script>

We use the v-carousel component with v-carousel-item s inside.

The background color for the slides are set with the color prop.

Conclusion

We can add a carousel and weather card with Vuetify.

Categories
Vuetify

Vuetify — Outlined and Floating Buttons

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Outlined Buttons

We can create outlined buttons with the outlined prop.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-btn class="ma-2" outlined color="indigo">Outlined Button</v-btn>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We can also create an outlined button with an icon inside:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-btn class="ma-2" outlined fab color="teal">
          <v-icon>mdi-format-list-bulleted-square</v-icon>
        </v-btn>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

Block Button

A block button extends the full width of the screen.

To add one, we can add the block prop:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-btn block color="secondary" dark>Block Button</v-btn>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

Button Loaders

We can display a loading indicator on the button to notify the user that processing is taking place.

The default behavior is to use a v-progress-circular component.

This can be customized.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-btn
          class="ma-2"
          :loading="loading"
          :disabled="loading"
          color="secondary"
          @click="loading = true"
        >Accept Terms</v-btn>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    loading: false,
  }),
};
</script>

Then we can see the loading indicator when we click the button.

loading is set to true so that the loading indicator is shown.

The loading prop is set to the loading state.

Buttons: Floating Action Button

We can add a gloating action button with the fab prop.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card>
          <v-toolbar color="light-blue" light extended>
            <v-app-bar-nav-icon></v-app-bar-nav-icon>
            <v-toolbar-title class="white--text">App</v-toolbar-title>
            <v-spacer></v-spacer>
            <v-btn icon>
              <v-icon>mdi-magnify</v-icon>
            </v-btn>
            <v-btn icon>
              <v-icon>mdi-view-module</v-icon>
            </v-btn>
            <template v-slot:extension>
              <v-btn fab color="cyan accent-2" bottom left absolute @click="dialog = !dialog">
                <v-icon>mdi-plus</v-icon>
              </v-btn>
            </template>
          </v-toolbar>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    dialog: false,
  }),
};
</script>

We add a v-btn into the extension slot so that it shows in the bottom left of the toolbar.

Also, we set the color prop and add the bottom and left props to make it show in the bottom left.

absolute makes it display with an absolute position.

Display Animation

We can display animation when showing and hiding floating action buttons.

For example, we can write:

<template>
  <v-container>
    <v-row class="text-center">
      <v-col col="12">
        <v-card>
          <v-toolbar extended>
            <v-app-bar-nav-icon></v-app-bar-nav-icon>
            <template v-slot:extension>
              <v-fab-transition>
                <v-btn v-show="!hidden" color="pink" fab dark small absolute bottom left>
                  <v-icon>mdi-plus</v-icon>
                </v-btn>
              </v-fab-transition>
            </template>
          </v-toolbar>
          <v-card-text style="height: 300px;" class="grey lighten-5 text-center">
            <v-btn color="primary" @click="hidden = !hidden">{{ hidden ? 'Show' : 'Hide' }}</v-btn>
          </v-card-text>
          <v-card-text style="height: 100px; position: relative">
            <v-fab-transition>
              <v-btn v-show="!hidden" color="pink" dark absolute top right fab>
                <v-icon>mdi-plus</v-icon>
              </v-btn>
            </v-fab-transition>
          </v-card-text>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    hidden: false,
  }),
};
</script>

We added the v-fab-transition prop into the extension slot to make the floating action button toggle on and off with animation.

The toggling is done with the hidden prop.

We use the small prop to make it small.

bottom and left position the button icons.

absolute makes the position absolute.

fab makes the button a floating action button.

Conclusion

We can display various kinds of buttons with Vuetify.