#13 Styling The App Shell And Removing A Model From The Vuex Store
Saturday, August 31, 2019
In this article we are going to start out by modifying the look of our app to make it look more app like. We will start by just defining a couple of colors that we can use for the background and foreground of the app bar and then proceed to use them to style the active button on our main nav bar. We will then finish up by adding functionality to our app so that a user can remove an account model by just clicking a button located in the accounts view.
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
Setting our app bar colors
- root
- src
- bitters
- _variables.scss
- bitters
- src
We are going to start by defining a couple of colors that we can use, not only for the app bar but in other locations as well, to bring some unification to the overall look and feel of our app (a).
_variables.scss
...
// App Bar Colors
$app-bar-background-color: get-swatch-color($blue-swatch, darken-3);
$app-bar-color: $light-gray;
...
Setting the theme of the app bar
- root
- src
- components
- app-bar
- TheAppBar.vue
- app-bar
- components
- src
Now that we have created our color variables we can use them to set the style of the app bar (b).
TheAppBar.vue
<style lang="sass" scoped>
...
@import "../../bitters/variables"
.app-bar
...
background-color: $app-bar-background-color
color: $app-bar-color
.angle-left
...
background-color: transparent
color: $app-bar-color
...
</style>
Our cog button looks out of place
- root
- src
- components
- app-bar
- TheAppBarSettings.vue
- app-bar
- components
- src
Next up we need to remove the background color from our settings button so that we can just have the background color of the app bar show through and to set the button's color (c).
TheAppBarSettings.vue
<style lang="sass" scoped>
...
.cog
...
background-color: transparent
color: $app-bar-color
</style>
The main nav needs some love
- root
- src
- components
- main-nav
- TheMainNav.vue
- main-nav
- components
- src
For the main nav component itself we are going to just add a small border to the top to separate it from the main content (d).
TheMainNav.vue
<style lang="sass" scoped>
@import "../../bitters/variables"
.main-nav
...
border-top: 1px solid $gray
</style>
Styling the active button (template)
- root
- src
- components
- main-nav
- MainNavButton.vue
- main-nav
- components
- src
From a users perspective it would be good for us to somehow indicate their current location within our app. The first way we are going to do this is to style our main nav buttons in such a way that it is obvious which one represents the current view (e). We do this the typical way by adding a conditional class to the button that points to the view that the user is currently viewing.
MainNavButton.vue
<template lang="pug">
button.main-nav-button(
v-bind:class="{ 'active': isActive }"
...)
...
</template>
Styling the active button (template)
- root
- src
- components
- main-nav
- MainNavButton.vue
- main-nav
- components
- src
Updating our isActive
field is fairly simple at this point, we start with it set to false and
initially update it within the created lifecycle hook. At that point we also subscribe to the
navigate stream so that when the user navigates to another view the field will be updated
automatically (f).
MainNavButton.vue
<script lang="ts">
...
export default class MainNavButton extends Vue {
...
private isActive = false;
...
private async created() {
this.isActive = await this.routingService.isCurrentRouteAsync(this.route);
this.routingService
.navigate$
.subscribe(this.setActive);
}
private setActive(route: Routes) {
this.isActive = this.route === route;
}
}
</script>
Styling the active button (style)
- root
- src
- components
- main-nav
- MainNavButton.vue
- main-nav
- components
- src
Now that our class is being applied we just need to add some styling to our buttons based on whether the button is currently active or not (g).
MainNavButton.vue
<style lang="sass" scoped>
...
@import "../../bitters/colors"
@import "../../bitters/variables"
.main-nav-button
...
background-color: transparent
color: $dark-gray
&:hover, &:focus
color: lighten($app-bar-background-color, 25%)
...
.button-label
...
cursor: pointer
.active
background-color: $app-bar-background-color
color: $app-bar-color
cursor: initial
&:hover, &:focus
color: $app-bar-color
label
cursor: initial
</style>
Adding a remove icon
- root
- src
- features
- font-awesome.ts
- features
- src
Now we are going to change gears and add the ability for the user to remove an account from the store. In order to do this we are going to need a button to click on and for that button we are going to use an icon. More specifically we are going to use the times icon which we need to import (h).
font-awesome.ts
...
import {
...
faTimes,
...
} from "@fortawesome/free-solid-svg-icons";
...
library.add(faTimes);
...
Adding the remove button (template)
- root
- src
- views
- Accounts.vue
- views
- src
As these things usually go it is time to update our template to include a remove button for each account model in our store (i).
Accounts.vue
<template lang="pug">
div.accounts-container
...
div.headings
label.name Name
div.remove
div.accounts(...)
div.name {{account.name}}
button.remove(v-on:click.prevent="remove(account.id)")
FontAwesomeIcon(icon="times")
</template>
Adding the remove button (script)
- root
- src
- views
- Accounts.vue
- views
- src
Initially we can just add the remove method to our script where it will just print the id that was passed into it to the console (j).
Accounts.vue
<script lang="ts">
...
export default class Account extends Vue {
...
private remove(id: number) {
console.log(id);
}
}
</script>
Adding the remove button (style)
- root
- src
- views
- Accounts.vue
- views
- src
Now we just need to add a little bit of styling (k).
Accounts.vue
<style lang="sass" scoped>
...
@import "../bitters/functions"
...
.headings, .accounts
...
display: flex
align-items: center
.name
flex: 1
...
.remove
min-width: rem(62px)
.accounts .remove
background-color: $red
&:hover, &:focus
background-color: darken($red, 10%)
color: white
</style>
Remove action and mutation constants
- root
- src
- store
- store-constants.ts
- store
- src
As we did in the previous article since we are going to be manipulating our store we first need to include a couple of constants, one for the action and one for the mutation (l).
store-constants.ts
...
export const ACTION_REMOVE_ACCOUNT = "ACTION_REMOVE_ACCOUNT";
export const MUTATION_REMOVE_ACCOUNT = "MUTATION_REMOVE_ACCOUNT";
Specifying the payload
- root
- src
- store
- account-types.ts
- store
- src
For us to know which account model needs to be removed we are going to need to have a payload that will be passed into the action and then passed to the mutation. And again at least for now the payload is just a single type (m).
account-types.ts
...
export type RemoveAccountPayload = number;
...
Implementing the action and mutation
- root
- src
- store
- account-module.ts
- store
- src
With both the constants and payload defined we can now return to our account module and implement the actual action and mutation that will be responsible for removing an account from our store (n).
account-module.ts
...
import {
...
ACTION_REMOVE_ACCOUNT,
...
MUTATION_REMOVE_ACCOUNT,
} from "@/store/store-constants";
...
import {
...
RemoveAccountPayload,
} from "@/store/account-types";
...
export const actions = {
...
[ACTION_REMOVE_ACCOUNT](this: Store<IStoreState>, { commit }: AccountContext, id: RemoveAccountPayload) {
commit(MUTATION_REMOVE_ACCOUNT, id);
},
};
export const mutations = {
...
[MUTATION_REMOVE_ACCOUNT](state: IStoreState, payload: RemoveAccountPayload) {
state.accounts.items = state.accounts.items.filter((x) => x.id !== payload);
},
};
Now we can remove an account
- root
- src
- views
- Accounts.vue
- views
- src
Last up is updating our account view so that we call the remove action (o).
Accounts.vue
<script lang="ts">
import {
...
ACTION_REMOVE_ACCOUNT,
...
RemoveAccountPayload,
} from "@/store";
@Component
export default class Account extends Vue {
...
@Action(ACTION_REMOVE_ACCOUNT) private removeAccount!: (payload: RemoveAccountPayload) => void;
...
private remove(id: number) {
this.removeAccount(id);
}
}
</script>