#11 Adding Accounts Data To Our Vuex Store
Saturday, August 17, 2019
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.
Parts
- Part 45: Adjusting Shares
- Part 44: Plan Percentages
- Part 43: Home Securities
- Part 42: Updating Plans
- Part 41: Plan Details View
- Part 40: Portfolio Getters
- Part 39: Portfolio Plan
- Part 38: Portfolio Category
- Part 37: Account Securities
- Part 36: Account Transfer
- Part 35: View Account Security
- Part 34: Updating Deposit
- Part 33: View Account Deposit
- Part 32: Display Account Details
- Part 31: Account Getters
- Part 30: Deposits And Securities
- Part 29: Add Accounts Details
- Part 28: Refactoring Accounts
- Part 27: Add Security Models
- Part 26: Edit Security Details
- Part 25: View Security Details
- Part 24: Navigating To Details
- Part 23: Getters Validation
- Part 22: Query Parameters
- Part 21: Tab Entries
- Part 20: Tab Header
- Part 19: List View
- Part 18: Vuex Getters
- Part 17: End Domain Model
- Part 16: Start Domain Model
- Part 15: Pop Routes
- Part 14: Push Routes
- Part 13: Removing Accounts
- Part 12: Vuex (Decorators)
- Part 11: Vuex (Accounts)
- Part 10: The App Bar (Settings)
- Part 9: Remove Consumer Rxjs
- Part 8: The App Bar (Back)
- Part 7: Structuring Our App
- Part 6: Animation Between Views
- Part 5: Navigation Fade
- Part 4: Navigation Requests
- Part 3: Fade Animations (cont.)
- Part 2: Fade Animations
- Part 1: Splash Screen
Moving the store
- root
- src
- store
- store.ts
- store
- src
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: {
},
});
Make importing easier
- root
- src
- store
- index.ts
- store
- src
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";
Including the store in our application
- root
- src
- main.ts
- src
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";
...
Our first model
- root
- src
- store
- account-model.ts
- store
- src
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;
}
}
Update the index
- root
- src
- store
- index.ts
- store
- src
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";
...
The initial account state
- root
- src
- store
- account-types.ts
- store
- src
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[];
}
The initial account state
- root
- src
- store
- account-initial-state.ts
- store
- src
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,
};
Another exciting type file
- root
- src
- store
- store-types.ts
- store
- src
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;
}
Back to exporting
- root
- src
- store
- index.ts
- store
- src
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";
Adding the accounts to our global state
- root
- src
- store
- store.ts
- store
- src
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: {
},
});
Switching gears to display our accounts (template)
- root
- src
- views
- Accounts.vue
- views
- src
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>
Switching gears to display our accounts (script)
- root
- src
- views
- Accounts.vue
- views
- src
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>
Finally a new route to add
- root
- src
- components
- routing
- types.ts
- routing
- components
- src
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,
...
}
Defining the actual accounts route
- root
- src
- routing
- routing-service.ts
- routing
- src
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,
...
];
...
}
Updating our buttons (template)
- root
- src
- components
- main-nav
- TheMainNav.vue
- main-nav
- components
- src
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>
Updating our buttons (template)
- root
- src
- components
- main-nav
- TheMainNav.vue
- main-nav
- components
- src
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>
Back to displaying our accounts (template)
- root
- src
- views
- Account.vue
- views
- src
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>
Back to displaying our accounts (template)
- root
- src
- views
- Account.vue
- views
- src
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>
Adding just a little touch of styling
- root
- src
- views
- Account.vue
- views
- src
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>