Vue intermediate

Vue router

Client-side routing

client-side routing: navigating between views without leaving the app

Client side routing

options:

hash-based client-side routing, e.g.:

  • example.com/#/home
  • example.com/#/shop/cart

client-side routing based on on the history API, e.g.:

  • example.com/home
  • example.com/shop/cart

for the second method, the server needs additional configuration

Route configuration

// router/index.js

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
  },
  {
    path: '/about',
    name: 'About',
    component: About,
  },
];

Basic components

<router-link to="/">Home</router-link>
<router-view />

Passing data

we can pass down data to the route:

<router-view :todos="todos" />

Route parameters

const routes = [
  {
    path: '/todos/:todoId',
    component: TodoDetailView,
  },
];

access the parameter:

Details of todo: {{ $route.params.todoId }}

Programmatic navigation

this.$router.push('/');

Styling links

active links will receive the .router-link-active class

Two-way binding in custom components

Two-way binding in custom components

(this is for Vue 3)

Two-way binding in custom components

two-way binding for inputs:

<input v-model="newTitle" />

two-way binding in custom components:

<star-rating v-model="productRating" />
<todo-item
  v-model:title="todo.title"
  v-model:completed="todo.completed"
/>

Two-way binding in custom components

implementation of star-rating:

  • prop: modelValue
  • event: update:modelValue

Two-way binding in custom components

implementation of todo-item:

  • props: title, completed
  • events: update:title, update:completed
<li>
  {{ completed ? "DONE: " : "TODO: " }}
  <input
    :value="title"
    @input="$emit('update:title', $event.target.value)"
    :style="{ border: 'none' }"
  />
  <button @click="$emit('update:completed', !completed)">
    toggle
  </button>
</li>

Prop types and prop validation

Prop types and prop validation

minimal prop definition of a component:

export default {
  props: ['title', 'completed'],
};

Prop types and prop validation

export default {
  props: {
    title: String,
    completed: Boolean,
  },
};

Prop types and prop validation

export default {
  props: {
    title: {
      type: String,
      required: true,
    },
    completed: {
      type: Boolean,
      default: false,
    },
  },
};

Vue and TypeScript

Vue and TypeScript

setting up a Vue TypeScript project:

during project creation with Vue-CLI, choose "Manually select features"

Vue and TypeScript

<script lang="ts">
  // ...
</script>

Vue and TypeScript

for better type annotations support:

export default {
  name: 'TodoApp',
  // ...
};

becomes:

import { defineComponent } from 'vue';

export default defineComponent({
  name: 'TodoApp',
  // ...
});

Vue and TypeScript

configuration of Vetur in VS Code:

Activate option: Vetur > Experimental: Template Interpolation Service

Vue and TypeScript

declaring the structure of the component state:

type ComponentState = {
  todos: Array<Todo>;
};
export default defineComponent({
  data(): ComponentState {
    // ...
  },
});

Vue and TypeScript

defining props with TypeScript annotations:

import { defineComponent, PropType } from 'vue';

// define interface / type "Todo" here

export default defineComponent({
  props: {
    type: [] as PropType<Array<Todo>>,
    required: true,
  },
});

Vue and TypeScript

Vue 3 only:

defining events and event payloads (inside validator functions):

export default defineComponent({
  emits: {
    /* eslint-disable @typescript-eslint/no-unused-vars */
    delete(id: number) {
      return true;
    },
    toggle(id: number) {
      return true;
    },
    /* eslint-enable @typescript-eslint/no-unused-vars */
  },
});

State management with VueX

State management

In more complex applications or components it makes sense to manage the state (model) separately from the view.

Often the entire application state is represented by a data model and every change to the state will be done by triggering a change to the data model.

State management with mutations

a mutation describes some action that took place in the application (in other tools: action)

a mutation has a type and potentially a payload property

State management with mutations

example mutations as they will appear in the devtools:

{
  "type": "addTodo",
  "payload": {
    "title": "learn Vue"
  }
}
{
  "type": "deleteCompletedTodos"
}

Example: todos state management

Manual use of a Vuex store:

const store = new Vuex.Store({
  // set up store here
});

store.commit('addTodo', { title: 'foo' });
store.commit('addTodo', { title: 'bar' });
store.commit('setTodoCompleted', {
  id: 1,
  completed: true,
});
store.commit('deleteTodo', { id: 2 });

console.log(JSON.stringify(store.state));

State management with Vuex

configuration of Vuex:

  • initial state
  • getters (similar to computed)
  • mutations (define state changes)
  • actions (asynchronous triggers of mutations)

State management with Vuex

const store = new Vuex.Store({
  state: { todos: [] },
  getters: {
    numIncomplete(state) {
      return state.todos.filter((t) => !t.completed).length;
    },
  },
  mutations: {
    addTodo(state, payload) {
      // ...
    },
    deleteTodo(state, payload) {
      // ...
    },
  },
  actions: {
    async loadFromApi(context) {
      // ...
    },
  },
});

Vuex and Vue

Using a Vuex store in a Vue app:

new Vue({
  // ...
  store: store,
});

The store will be available via $store inside components

Vuex and Vue

Accessing the Vuex state from components:

<todo-item v-for="todo in $store.state.todos" ... />
<p>Incomplete todos: {{ $store.getters.numIncomplete }}</p>

Vuex and Vue

Triggering mutations from Vue:

<todo-item
  :todo="todo"
  @toggle="$store.commit('toggleTodo', { id: todo.id })"
  @delete="$store.commit('deleteTodo', { id: todo.id })"
/>

Vuex and Vue

Triggering an action from Vue:

export default {
  async created() {
    this.$store.dispatch('loadFromApi');
  },
};

Transitions

Transitions

animation of enter / leave transitions:

<transition name="fade">
  <button>delete</button>
</transition>

use name fade for transition

Transitions

defining fade transition:

during animation:

.fade-enter-active {
  transition: opacity 5s;
}
.fade-leave-active {
  transition: opacity 5s;
}

start / end styles:

.fade-enter {
  opacity: 0;
}
.fade-leave-to {
  opacity 0;
}

Transition groups

transitions for groups of elements where individual elements can appear / disappear

<transition-group name="list-fade" tag="ul">
  <li v-for="todo in todos" :key="todo.id">
    {{ todo.title }}
  </li>
</transition-group>

Transition groups

.list-fade-enter-active,
.list-fade-leave-active {
  transition: opacity 0.5s;
}
.list-fade-enter,
.list-fade-leave-to {
  opacity: 0;
}

Transitions

documentation: https://v3.vuejs.org/guide/transitions-overview.html

Render functions

Render functions

concept from React: JSX - combination of JavaScript and XML

Render functions may be used as an alternative to templates

Render function

export default {
  name: 'Foo',
  render() {
    return (
      <div>
        foo
        <button>bar</button>
      </div>
    );
  },
};