Categories
Vue 3

Vue 3 — Teleport

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to use the teleport component to render elements and components in a different location in the DOM.

Teleport

We can use the teleport component to let us render parts of our Vue template in a location that’s different from its usual location in the DOM

This is handy for creating things like modals and overlays.

The DOM element that we want to render our items in must already exist.

Otherwise, we’ll get an error.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <teleport to="#footer">
        <p>footer</p>
      </teleport>
    </div>

    <div id="footer"></div>

    <script>
      const app = Vue.createApp({});

      app.mount("#app");
    </script>
  </body>
</html>

We added the teleport component to our template with the to prop set to the selector to mount the content inside it.

Therefore, the div with ID footer will hold the p element that’s inside the teleport component.

Using with Vue components

If teleport has a Vue component, then it’ll remain a child component of the teleport ‘s parent.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
<teleport to="#modals">
  <div>A</div>
</teleport>
<teleport to="#modals">
  <div>B</div>
</teleport>
    </div>

    <div id="footer"></div>

    <script>
      const app = Vue.createApp({});

      app.component("parent-component", {
        template: `
          <h2>parent</h2>
          <teleport to="#footer">
            <child-component name="james" />
          </teleport>
        `
      });

      app.component("child-component", {
        props: ["name"],
        template: `
          <div>{{ name }}</div>
        `
      });

      app.mount("#app");
    </script>
  </body>
</html>

We put the parent-component in the root template of our app.

It has a teleport that has to set to #footer .

So it’ll be rendered in the div with the ID footer .

It has the child-component inside it.

And that’ll be rendered inside div with ID footer also.

Using Multiple Teleports on the Same Target

We can have multiple teleports with the same selector set as their to prop value.

They’ll be rendered by in the order that they’re defined in.

For instance, if we have:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <teleport to="#footer">
        <div>foo</div>
      </teleport>
      <teleport to="#footer">
        <div>bar</div>
      </teleport>
    </div>

    <div id="footer"></div>

    <script>
      const app = Vue.createApp({});

      app.mount("#app");
    </script>
  </body>
</html>

Then the rendered result would be:

<div id="footer">
  <div>foo</div>
  <div>bar</div>
</div>

Conclusion

The teleport component lets us render parts of a Vue template in a location that’s different from the usual location in the DOM.

Categories
Vue 3

Vue 3 — Render Functions Basics

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to create render functions with Vue 3.

Render Functions

Templates should be used most of the time to build Vue components.

However, there may be cases where we need more flexibility.

For instance, we may want to add elements with dynamic tags.

Instead of writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <variable-heading :level="1">foo</variable-heading>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("variable-heading", {
        template: `
          <h1 v-if="level === 1">
            <slot></slot>
          </h1>
          <h2 v-else-if="level === 2">
            <slot></slot>
          </h2>
          <h3 v-else-if="level === 3">
            <slot></slot>
          </h3>
          <h4 v-else-if="level === 4">
            <slot></slot>
          </h4>
          <h5 v-else-if="level === 5">
            <slot></slot>
          </h5>
          <h6 v-else-if="level === 6">
            <slot></slot>
          </h6>
        `,
        props: {
          level: {
            type: Number,
            required: true
          }
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We can make the heading tags dynamic with a render function.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <variable-heading :level="1">foo</variable-heading>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("variable-heading", {
        render() {
          const { h } = Vue;

          return h(`h${this.level}`, {}, this.$slots.default());
        },
        props: {
          level: {
            type: Number,
            required: true
          }
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We created the variable-heading component with a render method to render our HTML.

The h method lets us render our items.

The first argument is the tag name.

The 2nd is any props or attributes we want to pass to it.

And the 3rd argument is an array fo children.

this.$slots.default() is the default slot to let us populate content,

The rest of the component are the same as the other parts.

Every element in the DOM is a node, and the h method lets us render the nodes by nesting them as we do in a tree.

The Virtual DOM Tree

Vue keeps a virtual DOM tree to keep track of changes is needs to make to the real DOM.

The h method returns an object that has the information needed for Vue to update the read DOM.

h() Arguments

The h method takes 3 arguments.

The tag name is the first argument. It can be a string, object, function, or null .

If it’s an object, then it must be a component or async component.

The 2nd argument is an object with optional props.

The 3rd argument is a string, array, or object of child nodes.

It’s also optional.

VNodes Must Be Unique

We can’t have duplicate VNodes in our render function.

So we can’t have:

render() {
  const myParagraphVNode = Vue.h('p', 'hi')
  return Vue.h('div', [
    paragraph, paragraph
  ])
}

We can’t have 2 paragraph s in the array.

Conclusion

We can use render functions to tell Vue how to render components with JavaScript.

This is an alternative to templates.

Categories
Vue 3

Vue 3 — More Complex Render Functions

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at how to create render functions with Vue 3.

Replacing Template Features with Render Functions

We can replace template features with render functions.

Directives like v-if and v-for can be replaced within render functions.

v-if can be replaced an if statement.

v-for can be replaced with an array of items.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <todo-list :todos="todos"></todo-list>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            todos: [{ name: "eat" }, { name: "drink" }, { name: "sleep" }]
          };
        }
      });

      app.component("todo-list", {
        props: ["todos"],
        render() {
          if (this.todos.length) {
            return Vue.h(
              "div",
              this.todos.map(todo => {
                return Vue.h("div", todo.name);
              })
            );
          } else {
            return Vue.h("div", "no todos.");
          }
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We created the todo-list component with the if statement to check the length fo the this.todos array.

And we called map to return an array of divs with the name .

v-on

The equivalent of the v-on directive is the on methods in the object in the 2nd argument.

For example, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <custom-div></custom-div>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("custom-div", {
        render() {
          return Vue.h(
            "div",
            {
              onClick: $event => console.log("clicked", $event.target)
            },
            "click me"
          );
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We created a custom-div component with a div.

The 2nd argument is an object that has the onClick method, which listens to clicks on the div.

The 3rd argument has the content between the div tags.

v-model

v-model ‘s equivalent is the modelValue prop and the onInput method.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <custom-input v-model="value"></custom-input>
      <p>{{value}}</p>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            value: ""
          };
        }
      });

      app.component("custom-input", {
        props: ["modelValue"],
        render() {
          return Vue.h("input", {
            modelValue: this.modelValue,
            onInput: ev => this.$emit("update:modelValue", ev.target.value)
          });
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

We have the modelValue prop with the custom-input component.

this.modelValue has the prop’s value.

We set that as the modelValue property’s value.

And then we emit the update:modelValue event to send the value to the parent.

Now when we type in something, it’ll be synchronized with the value state.

So we’ll see what we typed displayed.

Event Modifiers

Event modifiers can also be replaced with render functions.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
  </head>
  <body>
    <div id="app">
      <custom-div></custom-div>
    </div>
    <script>
      const app = Vue.createApp({});

      app.component("custom-div", {
        render() {
          return Vue.h(
            "div",
            {
              onClick: {
                handler: ev => console.log(ev),
                capture: true
              },
              onKeyUp: {
                handler: ev => console.log(ev),
                once: true
              },
              onMouseOver: {
                handler: ev => console.log(ev),
                once: true,
                capture: true
              }
            },
            "click me"
          );
        }
      });

      app.mount("#app");
    </script>
  </body>
</html>

to add the event handler methods.

The options are the modifiers.

Conclusion

We can replace various directives with their equivalents with render functions.

Categories
Vue 3

Vue 3 — Transition Between Components and Lists

Vue 3 is in beta and it’s subject to change.

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at creating transition effects between components and lists.

Transitioning Between Components

We can transition between components with the transition and component components.

The component component is used for switching between components dynamically by setting their name.

For instance, we can use it by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <style>
      .bounce-enter-active {
        animation: bounce-in 1.3s;
      }
      .bounce-leave-active {
        animation: bounce-in 1.3s reverse;
      }
      @keyframes bounce-in {
        0% {
          transform: scale(0);
        }
        50% {
          transform: scale(1.8);
        }
        100% {
          transform: scale(1);
        }
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="currentComponent = 'foo'">foo</button>
      <button @click="currentComponent = 'bar'">bar</button>
      <button @click="currentComponent = 'baz'">baz</button>
      <transition name="bounce" mode="out-in">
        <component :is="currentComponent"></component>
      </transition>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            currentComponent: "foo"
          };
        },
        components: {
          foo: {
            template: "<div>foo</div>"
          },
          bar: {
            template: "<div>bar</div>"
          },
          baz: {
            template: "<div>baz</div>"
          }
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

We created 3 components, foo , bar and baz .

And we to transition between them, we created 3 buttons to change the component names.

We also have the name prop and to set transition name.

And we also have the transition styles in the style tag.

When we click the buttons, we’ll see a transition effect before we see the content of the new component.

List Transitions

We can add transition effects to lists.

To do this, we can use the transition-group component.

Unlike transition , it renders the actual element.

A span is rendered by default.

We can change this with the tag attribute.

Transition modes aren’t available since we aren’t alternating between mutually exclusive elements.

Elements inside are always required to have a unique key attribute.

CSS transitions classes will be applied to inner elements and not to the container itself.

List Entering/Leaving Transitions

We can add a list enter or leave transitions with the transition-group component.

For instance, we can write:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <style>
      .list-enter-active,
      .list-leave-active {
        transition: all 1s ease;
      }
      .list-enter-from,
      .list-leave-to {
        opacity: 0;
        transform: translateY(35px);
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="add">Add</button>
      <button @click="remove">Remove</button>
      <transition-group name="list" tag="div">
        <p v-for="item in items" :key="item">
          {{ item }}
        </p>
      </transition-group>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            items: Array(10)
              .fill()
              .map(() => Math.random()),
            nextNum: 10
          };
        },
        methods: {
          randomIndex() {
            return Math.floor(Math.random() * this.items.length);
          },
          add() {
            this.items.splice(this.randomIndex(), 0, Math.random());
          },
          remove() {
            this.items.splice(this.randomIndex(), 1);
          }
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

We created a list from an array of random numbers.

And we added the add and remove buttons to let us add and remove items.

The transition effects are in the style tags.

The effects we added is the opacity change and vertical translation effects.

In the template, we have the transition-group component with the name prop set to list to make that the prefix of the transition class names.

tag is the tag we render for the container.

We added the key prop as required in the p element, which is rendered from the items list.

This way, Vue can tell where each item is and animate them correctly.

Finally, we have the add and remove methods to let us add and remove items from the items array.

randomIndex generates a random index to add or remove items.

Now when we click add or remove, we’ll see the transition effects applied.

Conclusion

We can transition between components and add transition effects to list items.

Categories
Vue 3

Vue 3 — Transitions

Vue 3 is the up and coming version of Vue front end framework.

It builds on the popularity and ease of use of Vue 2.

In this article, we’ll look at parts of a transition.

Timing

UI transitions have the timing to let us set the duration of the animation.

We may have different timing for transitions between different states.

If a transition has no intermediate state, then the timing is between 0.1s and 0.4s.

Easing

The easing lets us add some depth to our animation.

Without easing, the animation would be linear and uninteresting.

We want to use ease-out for entrances and and ease-in for exits.

Enter & Leave Transitions

We can use the transition wrapper component to add our enter and leave transitions.

For instance, we can add a simple transition with the transition component by writing:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://unpkg.com/vue@next"></script>
    <style>
      .fade-enter-active,
      .fade-leave-active {
        transition: opacity 1s ease;
      }

      .fade-enter-from,
      .fade-leave-to {
        opacity: 0;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>

      <transition name="fade">
        <p v-if="show">hello</p>
      </transition>
    </div>
    <script>
      const app = Vue.createApp({
        data() {
          return {
            show: false
          };
        }
      });
      app.mount("#app");
    </script>
  </body>
</html>

We add the transition to the p element by wrapping it with the transition component.

Then we set the name prop of it to the prefix of the classes we’ll use for styling the transition.

We set it to fade , so all the transition classes starts with fade- .

We have the fade-enter-active , fade-leave-active classes to show a fading effect.

And we have the fade-enter-from and fade-leave-to classes to make the content transparent to maker the content disappear when show is false .

Vue will detect whether the target element has CSS transitions or animations applied.

CSS transition classes will be added or removed at appropriate timings.

If no CSS transitions or animations are detected and no JavaScript hooks are provided, then ODM operations will be run on the next browser animation frame.

Transition Classes

There’re several transition classes that we can apply styles to.

v-enter-from is applied before the element is inserted and remove one frame after the element is inserted.

v-enter-active is applied before the animation is inserted and removed when the transition or animation finishes.

v-enter-to is added one frame after the element is inserted and removed when the transition or animation finishes.

v-leave-from is applied immediately when a leaving transition is triggered and removed after 1 frame.

v-leave-active is applied during the entire leaving phase.

It’s added immediately when the leave transition is triggered and removed when the transition or animation finishes.

v-leave-to is added one frame after a leaving transition is triggered and removed when the transition or animation finishes.

Conclusion

We can add transitions easily with the transition component.

Once we used that, we can add CSS style to apply the effects we want.