Skip to content

Vue 02

Pinia state management

  • https://pinia.vuejs.org/

Create the root store, and pass it to app

1
2
3
4
5
6
import { createPinia } from 'pinia'

app.use(createPinia());

app.use(router);
app.mount("#app");

Pinia - STORE

A Store is an entity holding state and business logic that isn't bound to your Components.
Pinia store has three concepts: the state, getters and actions.

Store setup can be done 2 ways - similar ways to Vue's options api or composition api.

A store should contain data that can be accessed throughout your application. This includes data that is used in many places, e.g. User information that is displayed in the navbar, as well as data that needs to be preserved through pages, e.g. a very complicated multi-step form.

Pinia - Define store

Setup Store

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'


export const useNumberStore = defineStore('number', () => {
  const count = ref(0)

  const doubleNumber = computed(() => count.value * 2)

  const increment = () => count.value++

  const incrementBy = (val: number) => count.value += val

  return { count, doubleNumber, increment, incrementBy }
})

OR Option Store

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { defineStore } from "pinia";

export const useCounterStore = defineStore({
  id: "counter",
  state: () => ({
    counter: 0,
  }),
  getters: {
    doubleCount: (state) => state.counter * 2,
  },
  actions: {
    increment() {
      this.counter++;
    },
  },
});

In Setup Stores:

  • ref()s become state properties
  • computed()s become getters
  • function()s become actions

Pinia - Using store

Import and use store in your component.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script setup lang="ts">
import { useNumberStore } from './stores/counter';

const store = useNumberStore();

store.$subscribe((mutation, state) => {
    console.log(mutation, state);
    console.log(state.count);
});

const changeState = () => {
    store.count = -1;
}
</script>

<template>
    <div>{{ store.count }}</div>
    <button @click="store.increment()">+1</button>
    <button @click="store.incrementBy(5)">+5</button>
    <button @click="changeState()">set to -1</button>
</template>

<style scoped>
</style>

Destructing store

Use storeToRefs().

1
2
const store = useNumberStore();
const { count, doubleNumber } = storeToRefs(store)

Template Refs

Direct access to the underlying DOM elements.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<script setup>
import { ref, onMounted } from 'vue'

const input = ref(null)

onMounted(() => {
    input.value.focus()
})
</script>

<template>
    <input ref="input" />
</template>

Vue router

If needed later then npm install vue-router@4. Or include it when creating new project.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import { createApp } from 'vue'
import { createPinia } from 'pinia'

import App from './App.vue'
import router from './router'

import './assets/main.css'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')
  • Globally registering the RouterView and RouterLink components.
  • Adding the global $router and $route properties.
  • Enabling the useRouter() and useRoute() composables.
  • Triggering the router to resolve the initial route.

Router component

  • https://router.vuejs.org
  • Main navigation between different sections in app are provided in Router component - src/router/index.ts
  • Selected component is rendered to <RouterView />

Router

src/router/index.ts

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import OwnersEdit from '../views/owners/Edit.vue'

const routes: Array<RouteConfig> = [
    {
        path: '/', name: 'Home', component: Home
    },
    // ==================== Owners ====================
    {
        path: '/owners', name: 'OwnersIndex', 
        // lazy loading chunk
        component: () => import("../views/owners/Index.vue")
    },
    // When props is set to true, use defineProps macro in component
    {
        path: '/owners/edit/:id', name: 'OwnersEdit', component: OwnersEdit, props: true
    },
]

const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes
})

export default router;

Routing

1
2
3
4
{
    path: '/test/:id', name: 'Test',
    component: Test, props: true
},
  • Specify url paceholders with propertyName.
  • Include props: true to map from url placeholders to component props.

  • Catchall with *

Receiving propertiess

1
2
3
4
5
<script setup lang="ts">
const props = defineProps(['foo'])

console.log(props.foo)
</script>

or using object syntax

1
2
3
4
5
6
<script setup lang="ts">
const props = defineProps({
  title: String,
  likes: Number
})
</script>

RouterView

1
2
3
  <!-- route outlet -->
  <!-- component matched by the route will render here -->
  <router-view></router-view>

Router - navigation

  • Declarative

    • <RouterLink :to="'viewpath'">
    • <RouterLink :to="{name:'personsedit', params: {id: id}}">Edit</RouterLink>
  • Programmatic

    • router.push(…)
  • Argument can be string path or a location descriptor object
    • router.push(‘animals’)
    • router.push({name: ‘animals’, params: {id: ‘5’}, query: {foo: ‘bar’}})
    • router.go(n) – navigate n steps back or forward
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script setup lang="ts">
import { RouterLink, useRouter, useRoute } from 'vue-router';
const router = useRouter()
const route = useRoute()

const go = () => {
    router.push({
        name: 'second',
        params: { id: '5' },
        query: { foo: 'bar' }
    });
}
</script>

<template>
    Home

    <RouterLink :to="'second'">To Second View</RouterLink>
    <button @click="go()">second</button>
</template>

<style scoped></style>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<script setup lang="ts">
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()
const route = useRoute()

const props = defineProps({
    id: String,
})

</script>

<template>
    Second: "{{ id }}" - "{{ route.query.foo }}"
</template>

<style scoped></style>

Watching route changes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<script setup>
import { useRoute } from 'vue-router'
import { ref, watch } from 'vue'

const route = useRoute()
const userData = ref()

// fetch the user information when params change
watch(
  () => route.params.id,
  async newId => {
    userData.value = await fetchUser(newId)
  }
)
</script>