#7 Structuring Our App to Look Like an App
Monday, July 22, 2019
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.
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
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",
...,
},
...
}
Only import the icons that we need
- root
- src
- features
- font-awesome.ts
- features
- src
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);
Adding it all to our application
- root
- src
- main.ts
- src
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";
...
Creating an app bar
- root
- src
- components
- TheAppBar.vue
- components
- src
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>
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>
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>
Adding the app bar
- root
- src
- App.vue
- src
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>
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>
The main navigation button
- root
- src
- components
- main-nav
- MainNavButton.vue
- main-nav
- components
- src
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>
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>
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>
The main navigation bar
- root
- src
- components
- main-nav
- TheMainNav.vue
- main-nav
- components
- src
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>
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>
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>
Adding the main navigation
- root
- src
- App.vue
- src
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>
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>
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>
Finishing touches (Home view)
- root
- src
- views
- Home.vue
- views
- src
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>
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>
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>
Finishing touches (About view)
- root
- src
- views
- About.vue
- views
- src
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>
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>