Advertisement

#11 Adding Accounts Data To Our Vuex Store

In this article we take our first step into adding global state to our application by using Vuex. We will start by defining our first model class and using it in the definition of our state. As part of defining that state we will include some test data, if the application is running in development, so that we can have something to work with when we add a new view to display the contents of the state.

Moving the store

  • root
    • src
      • store
        • store.ts

When we originally created this project one of the options that we checked was that we wanted to use Vuex. By making that selection the vue tooling included a file at the root of our source folder with the name store.ts. We are going to end up with several files for our store so we will start by creating a new folder and placing the automatically generated file into it (a). While we are at it we are also going to modify the file so that we use a named export instead of a default one.

store.ts

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export const store = new Vuex.Store({
  state: {

  },
  mutations: {

  },
  actions: {

  },
});
(a) Contents of the auto generated store.ts file that we are moving to its own folder.

Make importing easier

  • root
    • src
      • store
        • index.ts

Of course we have seen it plenty of times before we are going to make use of an index file so that we can have an easier time importing the store interface, types and classes into our other files (b).

index.ts

export * from "@/store/store";
(b) The index file will make it easier for us to import store related items into our other files.

Including the store in our application

  • root
    • src
      • main.ts

Along with generating the store.ts file the vue tooling also imported the exported store into our application for us to use This was done in our main.ts which we now need to update since we changed the location of the file (c).

main.ts

...
import { store } from "@/store";
...
(c) Updating the import of our store into our main typescript file.

Our first model

  • root
    • src
      • store
        • account-model.ts

Our store is not very helpful right now as it does not contain any information. We are going to start addressing this problem by creating our first model (d).

account-model.ts

export interface IAccountConfig {
    id: number;
    name: string;
}

export class AccountModel {
    private _id: number;
    private _name: string;

    public get id() { return this._id; }
    public get name() { return this._name; }

    constructor(config: IAccountConfig) {
        this._id = config.id;
        this._name = config.name;
    }
}
(d) Creating our first model that we can use in our store.

Update the index

  • root
    • src
      • store
        • index.ts

We are going to need to have reference to our account model so we might as well add the export to our index file (e).

index.ts

export * from "@/store/account-model";
...
(e) Exporting the account model from our index file.

The initial account state

  • root
    • src
      • store
        • account-types.ts

Outside of the interface that is used solely for creating our store class I have decided to keep other interface and type definitions in separate files. We will start by creating a types file that will specify what our account state looks like (f). Our state will consist of an array of account models as well as an index that we will use to assign an id to any account model that we add to the state.

account-types.ts

import { AccountModel } from "@/store/account-model";

export interface IAccountState {
    index: number;
    items: AccountModel[];
}
(f) Using a separate type file to define what our account state looks like.

The initial account state

  • root
    • src
      • store
        • account-initial-state.ts

For everything to work correctly we need to define the initial account state. In the process we will make use of the node environment variable to decide whether we want to add some test data when defining this initial state (g).

account-initial-state.ts

import { AccountModel } from "@/store/account-model";
import { IAccountState } from "@/store/account-types";

const accounts: AccountModel[] = [];

let index = 1;
if (process.env.NODE_ENV === "development") {
    accounts.push(new AccountModel({ id: index, name: "name-1" }));
    index += 1;
    accounts.push(new AccountModel({ id: index, name: "name-2" }));
    index += 1;
}

export const initialState: IAccountState = {
    index,
    items: accounts,
};
(g) Defining the initial account state to include some test data if we are running in development.

Another exciting type file

  • root
    • src
      • store
        • store-types.ts

Sticking with the theme it is now time to add another type file that we can use to export the types and interfaces for our store (h). For now our state just consists of the account state.

store-types.ts

import { IAccountState } from "@/store/account-types";

export interface IStoreState {
    accounts: IAccountState;
}
(h) The store type file contains the interface that describes the structure of our state.

Back to exporting

  • root
    • src
      • store
        • index.ts

If you guessed that we would be returning to this file a few times during the process of making this application you would be absolutely correct. Time to export both the account and store types (i).

index.ts

...
export * from "@/store/account-types";
...
export * from "@/store/store-types";
(i) Exporting account types.
Advertisement

Adding the accounts to our global state

  • root
    • src
      • store
        • store.ts

We now have everything in place in order to add our accounts to the store. (j)

store.ts

...
import { initialState as accountState } from "@/store/account-initial-state";
import { IStoreState } from "@/store/store-types";
...
const state: IStoreState = {
    accounts: accountState,
};

export const store = new Vuex.Store({
    state,
    mutations: {

    },
    actions: {

    },
});
(j) Adding the accounts to our vuex store.

Switching gears to display our accounts (template)

  • root
    • src
      • views
        • Accounts.vue

To display our accounts we are going to need a new view to work with. As per the usual we will create a temporary template that can use to see that everything is working (k).

Accounts.vue

<template lang="pug">
div Accounts View
</template>
(k) Initial temporary template for our accounts view.

Switching gears to display our accounts (script)

  • root
    • src
      • views
        • Accounts.vue

Just like with the template our script will just contain the bare minimum so that we can see that it is working as well (l).

Accounts.vue

<script lang="ts">
import { Component, Vue } from "vue-property-decorator";

@Component
export default class Account extends Vue {
    
}
</script>
(l) Initial temporary script for our accounts view.

Finally a new route to add

  • root
    • src
      • components
        • routing
          • types.ts

Now that we have a new view in order for us to be able to visit it we need to define the route that will be used to display it. The first step in the process is to add a new value to our routes enum (m).

types.ts

export enum Routes {
    ...
    Accounts,
    ...
}
(m) Adding a new value to our routes enum for the accounts view.

Defining the actual accounts route

  • root
    • src
      • routing
        • routing-service.ts

With a new enum created we can specify the actual route object itself and add it to our routes array (n).

routing-service.ts

...
const accounts = new RouteEntry({
    component: () => import(/* webpackChunkName: "accounts" */ "../../views/Accounts.vue"),
    name: "accounts",
    parent: home,
    path: "/accounts",
    route: Routes.Accounts,
});

export class RoutingService {
    ...
    private readonly _routes = [
        ...
        accounts,
        ...
    ];
    ...
}
(n) Specifying the accounts route and adding it to our routes array.

Updating our buttons (template)

  • root
    • src
      • components
        • main-nav
          • TheMainNav.vue

Having a new view and the route that points to it does not do us any good if no one has any idea that it exists. To solve this we will just repurpose the button in our main navigation that is currently pointing to the about view (o).

TheMainNav.vue

<template lang="pug">
nav.main-nav
    ...
    MainNavButton(
        v-bind:icon="'wallet'"
        v-bind:label="'Accounts'"
        v-bind:route="accounts")
</template>
(o) Repurposing the about button to point to our new accounts view.

Updating our buttons (template)

  • root
    • src
      • components
        • main-nav
          • TheMainNav.vue

Our view is now referencing the accounts enum value so we now need to add it to our script (p).

TheMainNav.vue

<script lang="ts">
...
export default class TheMainNav extends Vue {
    ...
    private readonly accounts = Routes.Accounts;
    ...
}
</script>
(p) Adding the accounts enum to our script.

Back to displaying our accounts (template)

  • root
    • src
      • views
        • Account.vue

Finally we can return to actually displaying our accounts. We will start by adding to our starter template (q). In the next code snippet we will see how the account state is defined but for now we just need to use a v-for directive to iterate over the items.

Account.vue

<template lang="pug">
div
    h1 Accounts
    div.headings
        label Name
    div.accounts(v-for="account in accountState.items"
                 v-bind:key="account.id")
        div {{account.name}}
</template>
(q) Using the v-for directive to iterate over the account state items.

Back to displaying our accounts (template)

  • root
    • src
      • views
        • Account.vue

Since we have our store added to the root of our application in the main typescript file we have access to it through the $store field (r). For now to get access to it we will just use a computed property.

Account.vue

<script lang="ts">
...
import { IAccountState } from "@/store";
...
export default class Account extends Vue {
    private get accountState(): IAccountState {
        return this.$store.state.accounts;
    }
}
</script>
(r) Using a computed property to allow access to our accounts state in our template.

Adding just a little touch of styling

  • root
    • src
      • views
        • Account.vue

Last up for this article is just to add a small touch of styling to our accounts (s).

Account.vue

<style lang="sass" scoped>
@import "../bourbon/bourbon"
@import "../bitters/variables"

.accounts-container
    @include padding(4px)

.headings, .accounts
    border-bottom: 1px solid $light-gray
    @include padding(0.5rem null)
</style>
(s) Styling that we are adding to our accounts template.
Exciton Interactive LLC
Advertisement