Advertisement

#28 Refactoring The Accounts Module

In this article we return to the first module that we created which was our accounts module. Our focus is to refactor the code that we have written previously to use the additional functionality and conventions that we created when working on our securities module.

Code Snippets

store-constants.ts (1:13)

...
export const GETTER_ACCOUNT = "GETTER_ACCOUNT";
export const GETTER_ACCOUNTS = "GETTER_ACCOUNTS";
...
src ⟩ store ⟩ store-constants.ts

store-types.ts (1:54)

...
export interface IStoreState extends IAccountState, ISecurityState {
    [STATE_ROUTES]: IRouteState;
}
src ⟩ store ⟩ store-types.ts

account-types.ts (2:29)

import { GETTER_ACCOUNT, GETTER_ACCOUNTS, STATE_ACCOUNTS } from "@/store/store-constants";
...
export type GetterAccount = (id: number) => AccountModel;
...
export type PayloadAddAccount = AccountModel;
export type PayloadRemoveAccount = number;

export interface IAccountGetters {
    [GETTER_ACCOUNT]: GetterAccount;
    [GETTER_ACCOUNTS]: AccountModel[];
}

export interface IAccountModelState {
    index: number;
    items: AccountModel[];
}

export interface IAccountState {
    [STATE_ACCOUNTS]: IAccountModelState;
}
src ⟩ store ⟩ account-types.ts

functions.ts (4:33)

...
export function remove<T extends IStateItem>(state: IState<T>, id: number) {
    state.items = state.items.filter((x) => x.id !== id);
}
...
src ⟩ store ⟩ functions.ts

account-module.ts (5:52)

import { StoreActions, StoreActionValidator } from "@/store/store-action-validator";
...
import {
    ...
    GETTER_ACCOUNT,
    GETTER_ACCOUNTS,
    ...
} from "@/store/store-constants";

import { add, findById, remove, undefinedMessage } from "@/store/functions";

import { ... StoreGetterTree, ... } from "@/store/store-types";

import { IAccountGetters, IAccountState, PayloadAddAccount, PayloadRemoveAccount } from "@/store/account-types";

import { initialState as accountState } from "@/store/account-initial-state";

const storeActionValidator = new StoreActionValidator();

export const accountActions: StoreActionTree = {
    [ACTION_ADD_ACCOUNT](this, { commit }, payload: PayloadAddAccount) {
        commit(MUTATION_ADD_ACCOUNT, payload);
    },
    [ACTION_REMOVE_ACCOUNT](this, { commit }, payload: PayloadRemoveAccount) {
        commit(MUTATION_REMOVE_ACCOUNT, payload);
    },
};

export const accountGetters: StoreGetterTree = {
    [GETTER_ACCOUNT]: (state) => {
        return (id: number) => {
            const account = findById(state[STATE_ACCOUNTS], id)!;

            storeActionValidator
                .begin()
                .while(StoreActions.Getting)
                .throwIf(account)
                .isUndefined(undefinedMessage("account", id, state[STATE_ACCOUNTS].index));

            return account;
        };
    },
    [GETTER_ACCOUNTS]: (state, getters: IAccountGetters) => {
        return state[STATE_ACCOUNTS].items.map((x) => getters[GETTER_ACCOUNT](x.id));
    },
};

export const accountMutations: StoreMutationTree = {
    [MUTATION_ADD_ACCOUNT](storeState, payload: PayloadAddAccount) {
        add(storeState[STATE_ACCOUNTS], payload, (x) => x.name);
    },
    [MUTATION_REMOVE_ACCOUNT](storeState: IStoreState, payload: PayloadRemoveAccount) {
        remove(storeState[STATE_ACCOUNTS], payload);
    },
};

export const accountsState = {
    [STATE_ACCOUNTS]: accountState,
};
src ⟩ store ⟩ account-module.ts

Advertisement

account-model.ts (13:18)

export interface IAccountModelConfig {
    ...
}

export class AccountModel {
    ...
    public set id(id: number) {
        this._id = id;
    }
    ...
    public set name(name: string) {
        this._name = name;
    }

    constructor(config: IAccountModelConfig) {
        ...
    }
}
src ⟩ store ⟩ account-model.ts

account-initial-state.ts (14:06)

import { IAccountModelConfig, ... } from "@/store/account-model";
import { IAccountModelState } from "@/store/account-types";
...
function createAccount(id: number, config: Omit<IAccountModelConfig, "id">) {
    accounts.push(new AccountModel({ id, ...config }));
    return (id += 1);
}

let index = 1;
if (process.env.NODE_ENV === "development") {
    index = createAccount(index, { name: "name-1" });
    index = createAccount(index, { name: "name-2" });
}

export const initialState: IAccountModelState = {
    index,
    items: accounts,
};
src ⟩ store ⟩ account-initial-state.ts

store.ts (15:48)

...
import { ..., accountGetters, accountsState } from "@/store/account-module";
...
const getters = {
    ...accountGetters,
    ...,
};
...
const state: IStoreState = {
    ...accountsState,
    ...,
};
...
src ⟩ store ⟩ store.ts

Accounts.vue (16:24)

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

import ListView from "@/components/ListView.vue";
import { IRoute, Routes, RoutingService } from "@/components/routing";

import { AccountModel, GETTER_ACCOUNTS, Getter } from "@/store";

@Component({
    components: {
        ListView,
    },
})
export default class Accounts extends Vue {
    @Getter(GETTER_ACCOUNTS) private readonly accounts!: AccountModel[];
    @Inject() private readonly routingService!: RoutingService;

    private onClick(account: AccountModel) {
        console.log("click")
    }

    private onClickCreate() {
        console.log("click create")
    }

    private renderAccount(account: AccountModel) {
        return <label>{account.name}</label>;
    }
}
</script>
src ⟩ views ⟩ Accounts.vue

Accounts.vue (19:37)

<template lang="pug">
ListView(
    v-bind:items="accounts"
    v-bind:onClick="onClick"
    v-bind:onClickCreate="onClickCreate"
    v-bind:renderFn="renderAccount")
</template>
src ⟩ views ⟩ Accounts.vue

Accounts.vue

<style lang="sass" scoped>
@import "../bourbon/bourbon"
@import "../bitters/functions"
@import "../bitters/variables"
</style>
src ⟩ views ⟩ Accounts.vue

Exciton Interactive LLC
Advertisement