Advertisement

#13 Styling The App Shell And Removing A Model From The Vuex Store

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.

Setting our app bar colors

  • root
    • src
      • bitters
        • _variables.scss

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;
...
(a) Defining the app bar color and background color.

Setting the theme of the app bar

  • root
    • src
      • components
        • app-bar
          • TheAppBar.vue

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>
(b) Styling the app bar using our color variables.

Our cog button looks out of place

  • root
    • src
      • components
        • app-bar
          • TheAppBarSettings.vue

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>
(c) Removing the background color of our settings button and setting its color.

The main nav needs some love

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

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>
(d) Adding a top border to our main nav component.

Styling the active button (template)

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

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>
(e) Adding a conditional class to the button that points to the current view.

Styling the active button (template)

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

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>
(f) Adding the code necessary to updating whether the button is active.

Styling the active button (style)

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

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>
(g) Adding styling to our buttons so that a user can differentiate between an active and inactive button.
Advertisement

Adding a remove icon

  • root
    • src
      • features
        • font-awesome.ts

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);
...
(h) Importing the times icon to be used for our remove buttons.

Adding the remove button (template)

  • root
    • src
      • views
        • Accounts.vue

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>
(i) Adding a remove button to each of our account model rows.

Adding the remove button (script)

  • root
    • src
      • views
        • Accounts.vue

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>
(j) Console logging the id that is passed into the remove method.

Adding the remove button (style)

  • root
    • src
      • views
        • Accounts.vue

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>
(k) Adding the styling to incorporate the remove button to our rows.

Remove action and mutation constants

  • root
    • src
      • store
        • store-constants.ts

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";
(l) Adding constants for the action and mutation of removing an account model.

Specifying the payload

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

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;
...
(m) Specifying the type of the payload that needs to be passed to the action.

Implementing the action and mutation

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

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);
    },
};
(n) Implementing the remove action and mutation.

Now we can remove an account

  • root
    • src
      • views
        • Accounts.vue

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>
(o) Updating our view so that the remove action is called when the corresponding button is pressed.
Exciton Interactive LLC
Advertisement