#26 Editing Security Models Using Vuex Actions And Mutations
Saturday, November 30, 2019
In this article we will add the vuex actions and mutations that we need in order to edit all of our security models and have those changes persisted to our store. Once they are in place we will create a new vue component that we can use to add cancel and save buttons to any details page that we add to our application. These buttons will encapsulate common functionality such as navigating to the previous view when the user presses the save or cancel buttons. We will of course finish by adding these buttons to our security details page.
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
Code Snippets
store-constants.ts (1:07)
...
export const ACTION_UPDATE_SECURITY = "ACTION_UPDATE_SECURITY";
export const ACTION_UPDATE_SECURITY_CATEGORY = "ACTION_UPDATE_SECURITY_CATEGORY";
export const ACTION_UPDATE_SECURITY_MARKET = "ACTION_UPDATE_SECURITY_MARKET";
export const ACTION_UPDATE_SECURITY_SEGMENT = "ACTION_UPDATE_SECURITY_SEGMENT";
export const ACTION_UPDATE_SECURITY_TERRITORY = "ACTION_UPDATE_SECURITY_TERRITORY";
export const ACTION_UPDATE_SECURITY_TYPE = "ACTION_UPDATE_SECURITY_TYPE";
...
export const MUTATION_UPDATE_SECURITY = "MUTATION_UPDATE_SECURITY";
export const MUTATION_UPDATE_SECURITY_CATEGORY = "MUTATION_UPDATE_SECURITY_CATEGORY";
export const MUTATION_UPDATE_SECURITY_MARKET = "MUTATION_UPDATE_SECURITY_MARKET";
export const MUTATION_UPDATE_SECURITY_SEGMENT = "MUTATION_UPDATE_SECURITY_SEGMENT";
export const MUTATION_UPDATE_SECURITY_TERRITORY = "MUTATION_UPDATE_SECURITY_TERRITORY";
export const MUTATION_UPDATE_SECURITY_TYPE = "MUTATION_UPDATE_SECURITY_TYPE";
security-types.ts (2:11)
...
export type PayloadUpdateSecurity = SecurityModel;
export type PayloadUpdateSecurityCategory = SecurityCategoryModel;
export type PayloadUpdateSecurityMarket = SecurityMarketModel;
export type PayloadUpdateSecuritySegment = SecuritySegmentModel;
export type PayloadUpdateSecurityTerritory = SecurityTerritoryModel;
export type PayloadUpdateSecurityType = SecurityTypeModel;
...
security-module.ts (2:47)
import { Store } from "vuex";
...
import {
ACTION_UPDATE_SECURITY,
ACTION_UPDATE_SECURITY_CATEGORY,
ACTION_UPDATE_SECURITY_MARKET,
ACTION_UPDATE_SECURITY_SEGMENT,
ACTION_UPDATE_SECURITY_TERRITORY,
ACTION_UPDATE_SECURITY_TYPE,
...
MUTATION_UPDATE_SECURITY,
MUTATION_UPDATE_SECURITY_CATEGORY,
MUTATION_UPDATE_SECURITY_MARKET,
MUTATION_UPDATE_SECURITY_SEGMENT,
MUTATION_UPDATE_SECURITY_TERRITORY,
MUTATION_UPDATE_SECURITY_TYPE,
...
} from "@/store/store-constants";
...
import { ..., StoreActionTree, StoreContext, ..., StoreMutationTree } from "@/store/store-types";
...
import {
...
PayloadUpdateSecurity,
PayloadUpdateSecurityCategory,
PayloadUpdateSecurityMarket,
PayloadUpdateSecuritySegment,
PayloadUpdateSecurityTerritory,
PayloadUpdateSecurityType,
} from "@/store/security-types";
...
export const securitiesActions: StoreActionTree = {
[ACTION_UPDATE_SECURITY](this: Store<IStoreState>, { commit }: StoreContext, payload: PayloadUpdateSecurity) {
const category = (this.getters as ISecurityGetters)[GETTER_SECURITY_CATEGORY](payload.categoryId);
const market = (this.getters as ISecurityGetters)[GETTER_SECURITY_MARKET](payload.marketId);
payload.setCategory(category);
payload.setMarket(market);
commit(MUTATION_UPDATE_SECURITY, payload);
},
[ACTION_UPDATE_SECURITY_CATEGORY](
this: Store<IStoreState>,
{ commit }: StoreContext,
payload: PayloadUpdateSecurityCategory,
) {
const segment = (this.getters as ISecurityGetters)[GETTER_SECURITY_SEGMENT](payload.segmentId);
const territory = (this.getters as ISecurityGetters)[GETTER_SECURITY_TERRITORY](payload.territoryId);
const type = (this.getters as ISecurityGetters)[GETTER_SECURITY_TYPE](payload.typeId);
payload.setSegment(segment);
payload.setSegment(territory);
payload.setType(type);
commit(MUTATION_UPDATE_SECURITY_CATEGORY, payload);
},
[ACTION_UPDATE_SECURITY_MARKET](
this: Store<IStoreState>,
{ commit }: StoreContext,
payload: PayloadUpdateSecurityMarket,
) {
commit(MUTATION_UPDATE_SECURITY_MARKET, payload);
},
[ACTION_UPDATE_SECURITY_SEGMENT](
this: Store<IStoreState>,
{ commit }: StoreContext,
payload: PayloadUpdateSecuritySegment,
) {
commit(MUTATION_UPDATE_SECURITY_SEGMENT, payload);
},
[ACTION_UPDATE_SECURITY_TERRITORY](
this: Store<IStoreState>,
{ commit }: StoreContext,
payload: PayloadUpdateSecurityTerritory,
) {
commit(MUTATION_UPDATE_SECURITY_TERRITORY, payload);
},
[ACTION_UPDATE_SECURITY_TYPE](
this: Store<IStoreState>,
{ commit }: StoreContext,
payload: PayloadUpdateSecurityType,
) {
commit(MUTATION_UPDATE_SECURITY_TYPE, payload);
},
};
...
export const securitiesMutations: StoreMutationTree = {
[MUTATION_UPDATE_SECURITY](state: IStoreState, payload: PayloadUpdateSecurity) {
const security = findById(state[STATE_SECURITIES], payload.id);
storeActionValidator
.begin()
.while(StoreActions.Updating)
.throwIf(security)
.isUndefined(undefinedMessage("security", payload.id, state[STATE_SECURITIES].index));
security!.categoryId = payload.categoryId;
security!.last = payload.last;
security!.marketId = payload.marketId;
security!.recommendation = payload.recommendation;
security!.symbol = payload.symbol.toUpperCase();
},
[MUTATION_UPDATE_SECURITY_CATEGORY](state: IStoreState, payload: PayloadUpdateSecurityCategory) {
const category = findById(state[STATE_SECURITY_CATEGORIES], payload.id);
storeActionValidator
.begin()
.while(StoreActions.Updating)
.throwIf(category)
.isUndefined(undefinedMessage("category", payload.id, state[STATE_SECURITY_CATEGORIES].index));
category!.segmentId = payload.segmentId;
category!.territoryId = payload.territoryId;
category!.typeId = payload.typeId;
},
[MUTATION_UPDATE_SECURITY_MARKET](state: IStoreState, payload: PayloadUpdateSecurityMarket) {
const market = findById(state[STATE_SECURITY_MARKETS], payload.id);
storeActionValidator
.begin()
.while(StoreActions.Updating)
.throwIf(market)
.isUndefined(undefinedMessage("market", payload.id, state[STATE_SECURITY_MARKETS].index));
market!.text = payload.text.toUpperCase();
},
[MUTATION_UPDATE_SECURITY_SEGMENT](state: IStoreState, payload: PayloadUpdateSecuritySegment) {
const segment = findById(state[STATE_SECURITY_SEGMENTS], payload.id);
storeActionValidator
.begin()
.while(StoreActions.Updating)
.throwIf(segment)
.isUndefined(undefinedMessage("segment", payload.id, state[STATE_SECURITY_SEGMENTS].index));
segment!.text = payload.text;
},
[MUTATION_UPDATE_SECURITY_TERRITORY](state: IStoreState, payload: PayloadUpdateSecurityTerritory) {
const territory = findById(state[STATE_SECURITY_TERRITORIES], payload.id);
storeActionValidator
.begin()
.while(StoreActions.Updating)
.throwIf(territory)
.isUndefined(undefinedMessage("territory", payload.id, state[STATE_SECURITY_TERRITORIES].index));
territory!.text = payload.text;
},
[MUTATION_UPDATE_SECURITY_TYPE](state: IStoreState, payload: PayloadUpdateSecurityType) {
const type = findById(state[STATE_SECURITY_TYPES], payload.id);
storeActionValidator
.begin()
.while(StoreActions.Updating)
.throwIf(type)
.isUndefined(undefinedMessage("type", payload.id, state[STATE_SECURITY_TYPES].index));
type!.text = payload.text;
},
};
store.ts (13:37)
...
import { securitiesActions, ..., securitiesMutations, ... } from "@/store/security-module";
...
const actions = {
...
...securitiesActions,
};
...
const mutations = {
...
...securitiesMutations,
};
...
security-model.ts (14:13)
...
export class SecurityModel {
...
public get category() {
return this._category;
}
public get categoryId() {
return this._categoryId;
}
public set categoryId(id: number) {
this._categoryId = id;
}
public get id() {
return this._id;
}
public set id(id: number) {
this._id = id;
}
public get last() {
return this._last;
}
public set last(last: number) {
this._last = last;
}
public get market() {
return this._market;
}
public get marketId() {
return this._marketId;
}
public set marketId(id: number) {
this._marketId = id;
}
public get recommendation() {
return this._recommendation;
}
public set recommendation(recommendation: SecurityRecommendations) {
this._recommendation = recommendation;
}
public get symbol() {
return this._symbol;
}
public set symbol(symbol: string) {
this._symbol = symbol;
}
...
}
Advertisement
security-category-model.ts (14:40)
export class SecurityCategoryModel {
...
public get id() {
return this._id;
}
public set id(id: number) {
this._id = id;
}
public get segment() {
return this._segment;
}
public get segmentId() {
return this._segmentId;
}
public set segmentId(id: number) {
this._segmentId = id;
}
public get territory() {
return this._territory;
}
public get territoryId() {
return this._territoryId;
}
public set territoryId(id: number) {
this._territoryId = id;
}
public get text() {
const text = { text: "undefined" };
const segment = (this.segment || text).text;
const territory = (this.territory || text).text;
const type = (this.type || text).text;
return `${territory} ${type} ${segment}`;
}
public get type() {
return this._type;
}
public get typeId() {
return this._typeId;
}
public set typeId(id: number) {
this._typeId = id;
}
...
}
security-descriptors.ts (15:08)
export abstract class SecurityDescriptor {
...
public set text(text: string) {
this._text = text;
}
constructor(..., private _text: string) {}
...
}
SecurityDetails.vue (15:41)
<script lang="ts">
...
import {
...
ACTION_UPDATE_SECURITY,
ACTION_UPDATE_SECURITY_CATEGORY,
ACTION_UPDATE_SECURITY_MARKET,
ACTION_UPDATE_SECURITY_SEGMENT,
ACTION_UPDATE_SECURITY_TERRITORY,
ACTION_UPDATE_SECURITY_TYPE,
...
PayloadUpdateSecurity,
PayloadUpdateSecurityCategory,
PayloadUpdateSecurityMarket,
PayloadUpdateSecuritySegment,
PayloadUpdateSecurityTerritory,
PayloadUpdateSecurityType,
...
SecurityModel,
...
} from "@/store";
type ActionUpdateTerritory = ActionFn<PayloadUpdateSecurityTerritory>;
...
export default class SecurityDetails extends Vue {
...
@Action(ACTION_UPDATE_SECURITY) private readonly updateSecurity!: ActionFn<PayloadUpdateSecurity>;
@Action(ACTION_UPDATE_SECURITY_CATEGORY) private readonly updateCategory!: ActionFn<PayloadUpdateSecurityCategory>;
@Action(ACTION_UPDATE_SECURITY_MARKET) private readonly updateMarket!: ActionFn<PayloadUpdateSecurityMarket>;
@Action(ACTION_UPDATE_SECURITY_SEGMENT) private readonly updateSegment!: ActionFn<PayloadUpdateSecuritySegment>;
@Action(ACTION_UPDATE_SECURITY_TERRITORY) private readonly updateTerritory!: ActionUpdateTerritory;
@Action(ACTION_UPDATE_SECURITY_TYPE) private readonly updateType!: ActionFn<PayloadUpdateSecurityType>;
...
private save() {
switch (this.which) {
case SecurityDescriptors.Categories:
const category = new SecurityCategoryModel({
id: this.id,
segmentId: this.categorySegment!.id,
territoryId: this.categoryTerritory!.id,
typeId: this.categoryType!.id,
});
switch (this.id) {
case 0:
return;
default:
this.updateCategory(category);
return;
}
return;
case SecurityDescriptors.Markets:
const market = new SecurityMarketModel(this.id, this.text);
switch (this.id) {
case 0:
return;
default:
this.updateMarket(market);
return;
}
return;
case SecurityDescriptors.Securities:
const security = new SecurityModel({
categoryId: this.securityCategory!.id,
id: this.id,
last: this.securityLast,
marketId: this.securityMarket!.id,
recommendation: this.securityRecommendation,
symbol: this.securitySymbol,
});
switch (this.id) {
case 0:
return;
default:
this.updateSecurity(security);
return;
}
return;
case SecurityDescriptors.Segments:
const segment = new SecuritySegmentModel(this.id, this.text);
switch (this.id) {
case 0:
return;
default:
this.updateSegment(segment);
return;
}
return;
case SecurityDescriptors.Territories:
const territory = new SecurityTerritoryModel(this.id, this.text);
switch (this.id) {
case 0:
return;
default:
this.updateTerritory(territory);
return;
}
return;
case SecurityDescriptors.Types:
const type = new SecurityTypeModel(this.id, this.text);
switch (this.id) {
case 0:
return;
default:
this.updateType(type);
return;
}
return;
}
}
}
</script>
DetailsActionButtons.vue (21:50)
<script lang="ts">
import { Component, Inject, Prop, Vue } from "vue-property-decorator";
import { RoutingService } from "@/components/routing";
@Component
export default class DetailsActionButtons extends Vue {
@Inject() private readonly routingService!: RoutingService;
@Prop() private readonly save!: () => void;
private cancel() {
this.routingService.back();
}
private saveInternal() {
this.save();
this.routingService.back();
}
}
</script>
DetailsActionButtons.vue (23:15)
<template lang="pug">
div.buttons
button.red(
type="button"
v-on:click.prevent="cancel") Cancel
button.green(
type="submit"
v-on:click.prevent="saveInternal") Save
</template>
DetailsActionButtons.vue (24:11)
<style lang="sass" scoped>
.buttons
display: flex
button
flex: 1
</style>
SecuritiesDetails.vue (24:39)
<script lang="ts">
...
import DetailsActionButtons from "@/components/DetailsActionButtons.vue";
...
@Component({
components: {
DetailsActionButtons,
}
})
export default class SecuritiesDetails extends Vue {
...
}
</script>
SecuritiesDetails.vue (25:16)
<template lang="pug">
...
div.securities-details
form
...
DetailsActionButtons(v-bind:save="save")/
</template>
_buttons.scss (26:22)
...
@mixin button($color) {
$hover-color: shade($color, 20%);
background-color: $color;
&:hover {
background-color: $hover-color;
color: contrast-switch($hover-color);
}
&:disabled {
&:hover {
background-color: $color;
}
}
}
button.red {
@include button($red);
}
button.green {
@include button(get-swatch-color($green-swatch, darken-1));
}
Exciton Interactive LLC