Advertisement

#7 Structuring Our App to Look Like an App

In this article we will start adding support for including some svg icon to our application. While we are doing that we will see how we can include only the icons that we need instead of all of them. With that done we will turn to creating a couple of components that are mainstays of just about any application. Those being an app bar, which will be located at the top of our app and a navigation bar that will be at the bottom.

Adding an icon set

  • root
    • package.json

Our first step will be to install a few packages from npm related to using Font Awesome icons within our applications. You can see which packages we are installing and what there version numbers were at the time of the writing of this article (a).

package.json

{
    ...
    "dependencies": {
        "@fortawesome/fontawesome-svg-core": "^1.2.19",
        "@fortawesome/free-solid-svg-icons": "^5.9.0",
        "@fortawesome/vue-fontawesome": "^0.1.6",
        ...,
  },
    ...
}
(a) Font Awesome packages that we need to install and their associated version numbers.

Only import the icons that we need

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

The set of icons that we have now added to our project is very large and we do not want to just import them all into our bundles. We only want to add the icons that we need. We accomplish this goal by using a library (b). And while we are in the process of picking the icons that we are going to use we are also going to register the Vue component that we will use in order to display the appropriate icon.

font-awesome.ts

import Vue from "vue";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { library } from "@fortawesome/fontawesome-svg-core";
import {
    faAngleLeft,
    faCog,
    faHome,
    faWallet,
} from "@fortawesome/free-solid-svg-icons";

library.add(faAngleLeft);
library.add(faCog);
library.add(faHome);
library.add(faWallet);
Vue.component("FontAwesomeIcon", FontAwesomeIcon);
(b) Setting up a library which will contain only the icons that we want to use along with registering the Vue component we will use to display them.

Adding it all to our application

  • root
    • src
      • main.ts

The only thing left to do is to add everything that we have done to our application and that is accomplished by making one small change to our main file (c).

main.ts

...
import "@/features/font-awesome";
...
(c) Adding the icons to our app is as easy as adding a single import to our main file.

Creating an app bar

  • root
    • src
      • components
        • TheAppBar.vue

The first step in making our app look more like an app is to create an app bar that will live at the top of each page. We will start as usual by creating a template for the component (d).

TheAppBar.vue

<template lang="pug">
div.app-bar
    button.angle-left
        FontAwesomeIcon(
            icon="angle-left"
            size="lg")/
    div.title Portfolio Balancer
    button.cog
        FontAwesomeIcon(icon="cog")/
</template>
(d) The initial template for our app bar.

And as usual we will just create a stub for the component class definition (e).

TheAppBar.vue

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

@Component
export default class TheAppBar extends Vue {
    
}
</script>
(e) The class component definition stub for the app bar.

Last but not least we will add a bit of styling (f).

TheAppBar.vue

<style lang="sass" scoped>
@import "../bourbon/bourbon"

.app-bar
    display: flex

.angle-left, .cog
    flex: 0
    @include padding(0.75rem 1rem)

.title
    flex: 1
    display: flex
    align-items: center
    font-weight: 600
    font-size: 1.25rem
    @include padding(null 0.5rem)
</style>
(f) The initial styling for our app bar.

Adding the app bar

  • root
    • src
      • App.vue

Of course in order for us to see and use our app bar we need to first add it to our app by first updating the template (g).

App.vue

<template lang="pug">
div#app
    ...
    AppBar/
    ...
</template>
(g) Updating our app component's template to include the app bar.

In the script we just need to import and register the app bar (h).

App.vue

<script lang="ts">
...
import AppBar from "@/components/TheAppBar.vue";
...
@Component({
    components: {
        AppBar,
        ...,
    },
})
export default class App extends Vue {
    
}
</script>
(h) Importing and registering the app bar.

The main navigation button

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

Next up we will also need a way to perform navigation between all of our pages. At least for the main pages we will use a main navigation bar which will live at the bottom of each of our pages. This time though our component will include some sub-components, namely main navigation buttons. Again we start by adding in an initial template (i) for them.

MainNavButton.vue

<template lang="pug">
button(v-on:click.prevent="click")
    FontAwesomeIcon(v-bind:icon="icon")
    label {{label}}
</template>
(i) Initial template for our main navigation buttons.

Now we add our class definition but at least this time we can add just a bit of functionality to start with (j).

MainNavButton.vue

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

import { Routes, RoutingService } from "@/components/routing";

@Component
export default class MainNavButton extends Vue {
    @Inject() private readonly routingService!: RoutingService;

    @Prop() private readonly icon!: string;
    @Prop({ default: "undefined" }) private readonly label!: string;
    @Prop() private readonly route!: Routes;

    private click() {
        this.routingService.navigateTo(this.route);
    }
}
</script>
(j) The initial class definition for our main navigation button.

Of course the next thing that we want to do is to add some styling (k). Again as in the past we are going to use some helper function from the bourbon sass library. If you just do not want to install/use it you can just write out the styles by hand.

MainNavButton.vue

<style lang="sass" scoped>
@import "../../bourbon/bourbon"

.main-nav-button
    @include padding(0.75rem 1rem)

.button-icon
    margin-bottom: 0.25rem

.button-label
    margin-bottom: 0
</style>
(k) The initial styling for our main navigation button.
Advertisement

The main navigation bar

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

Now that our buttons are in place we can turn to creating the main navigation component. For now we will just recreate our ability to navigate between our home and about view (l).

TheMainNav.vue

<template lang="pug">
nav.main-nav
    MainNavButton(
        v-bind:icon="'home'"
        v-bind:label="'Home'"
        v-bind:route="home")
    MainNavButton(
        v-bind:icon="'wallet'"
        v-bind:label="'About'"
        v-bind:route="about")
</template>
(l) The initial template for our main navigation bar.

For everything to work all we need is to specify the enums for our home and about views (m).

TheMainNav.vue

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

import MainNavButton from "@/components/main-nav/MainNavButton.vue";

import { Routes } from "@/components/routing";

@Component({
    components: {
        MainNavButton,
    },
})
export default class TheMainNav extends Vue {
    private readonly about = Routes.About;
    private readonly home = Routes.Home;
 }
</script>
(m) Our main nav class just needs to contain the route enum values that we currently have defined.

The styling that we will start with is minimal and will just result in our buttons being centered (n).

TheMainNav.vue

<style lang="sass" scoped>
.main-nav
    display: flex
    justify-content: center
</style>
(n) The initial styling for our main navigation just centers the buttons.

Adding the main navigation

  • root
    • src
      • App.vue

Time for us to update our application by adding our new main navigation starting first with the template (o).

App.vue

<template lang="pug">
div#app
    ...
    AppBar.app-app-bar/
    RouterOutlet.app-router-outlet/
    MainNav.app-main-nav/
</template>
(o) Updating our app template to include our main nav component.

In the class definition we just need to register our new component (p).

App.vue

<script lang="ts">
...
import MainNav from "@/components/main-nav/TheMainNav.vue";
...
@Component({
    components: {
        ...,
        MainNav,
        ...,
    },
})
export default class App extends Vue {
    
}
</script>
(p) The app component definition just needs to declare our main nav.

To accomplish our goal of parking the app bar and the top of the app and the main nav at the bottom we will be using css grid (q).

App.vue

<style lang="sass" scoped>
...
#app
    ...
    text-align: center // <-- remove this
    ...
    display: grid
    grid-template-rows: auto minmax(0, 1fr) auto
    grid-template-areas: "app-app-bar" "app-router-outlet" "app-main-nav"

.app-app-bar
    grid-area: app-app-bar

.app-router-outlet
    grid-area: app-router-outlet
    overflow: auto

.app-main-nav
    grid-area: app-main-nav
</style>
(q) Using css grid to position all of our elements where we want them to be.

Finishing touches (Home view)

  • root
    • src
      • views
        • Home.vue

The last thing we will do in this article is clean up both our Home and About views. Starting with the home view template we will just keep the stock HelloWorld component (r).

Home.vue

<template lang="pug">
div.home
      HelloWorld(msg="Welcome to Your Vue.js + TypeScript App")/
</template>
(r) Paring down our home template to just bring in the HelloWorld component.

The class definition for the home component can now be emptied (s).

Home.vue

<script lang="ts">
...
import { Routes, RoutingService } from "@/components/routing";  // <-- Remove this
...
export default class Home extends Vue {
    @Inject() private readonly routingService!: RoutingService; // <-- Remove this

    private click() { // <-- Remove this
        this.routingService.navigateTo(Routes.About);
    }
}
</script>
(s) We can now remove all of the implementation details of our Home component.

For our styling we will just increase the vertical size of the home view so that we can make sure that we are able to scroll the main content area as we are expecting to (t).

Home.vue

<style lang="sass" scoped>
.home
    height: 2000px
</style>
(t) Artificially increasing the vertical size of our home view.

Finishing touches (About view)

  • root
    • src
      • views
        • About.vue

As with the Home view we can now remove our button from the template of our about view (u).

About.vue

<template lang="pug">
div.about
    h1 This is an about page
</template>
(u) The template of our about view just contains our h1 now.

For the class we can basically return it to a stub state (v).

About.vue

<script lang="ts">
...
import { Routes, RoutingService } from "@/components/routing";  // <-- Remove this
...
export default class About extends Vue {
    @Inject() private readonly routingService!: RoutingService; // <-- Remove this

    private click() { // <-- Remove this method
        this.routingService.navigateTo(Routes.Home);
    }
}
</script>
(v) Removing all of the unnecessary elements of our class definition.
Exciton Interactive LLC
Advertisement