Building a responsive blackboard application using Vue.js.

Vue describes itself as approachable, versatile and performant. Seems cool. So I decided to try it out. For this tutorial I created a Blackboard single page application to test run this JavaScript framework. The user interface is poor to say the least but it has some cool features without using a database. You can enter text in a small text box. You can then resize the text and save the page and created a new page. The text is show on the “blackboard” much larger than the textbox. You can review your saved pages create more pages.

As far as user interface goes while researching Vue I learnt about Vuetify.js. It is a great framework to add material styles to your app. You can learn more about that here. I didn’t go further because this was just a beginner overview. Also bootstrap as a Vue plugin or Vue has a bootstrap plugin. Check it out here.

Getting Started

To get started I searched out a starter template of course. I found this one using webpack https://github.com/vuejs-templates/webpack. And followed the instructions. Note you can find my code here.

$ npm install -g vue-cli
$ vue init webpack my-project
$ cd my-project
$ npm install
$ npm run dev

So once you have node and npm installed you should be good to go. This installed the project for you and gets you started. However if you’re using my completed app as your template you can do the following.

git clone https://github.com/wyntonfranklin/blackboard-vue-tutorial.git
npm install -g vue-cli // make sure vue-cli is installed
cd blackboard-vue-tutorial
npm install
npm run dev

The important part of the above is to ensure you have vue-cli installed globally. The dependencies will be handled with npm install command.

Directory Structure

build/                  
config/
src/
   assets/
   components/
   router/
   App.vue
   main.js

Main.js

The main.js has the start up code for you Vue application. It creates a Vue instance and registers any components that you have.

import App from './App'
import router from './router'

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

Here I import the App.vue files and the router which we will get to. The components object holds the imported App and the router is also added.

App.vue

A Vue component files consists of three parts. A template, a script and a style section.

<template>
// your html here
  <div id="app">
    <h1>BlackBoard Vue Tutorial</h1>
    <router-view/>
  </div>
</template>

<script>
// your javascript here
export default {
  name: 'App'
}
</script>

<style>
 // your styles here
</style>

In the App.vue I have my global CSS styles and the main HTML template. Note the <router-view/> tag. It is under the header. This allows for us to using routing. It is a single page app but I created a second page and used routing just to show how routing workings in Vue.

The Router

You don’t need to use a router in Vue but I installed one when I created the template via cli. Routing is for going to different pages. The router file instance is located in the router folder and the file is name index.js.

// router/index.js file

import Router from 'vue-router'
import BlackBoard from '@/components/BlackBoard'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'BlackBoard',
      component: BlackBoard
    }
  ]
})

Here I import the router object and the blackboard Vue from the components folder. I then request that the middleware router is to be used , next I export a new Router object that as an array called routes. The routes array takes objects that require a path, a name and a component. With this done I can now access my blackboard.vue from the “/” path.

To add a new route I simply added a new object to the routes array.

// router/index.js file

import ContactView from '@/components/ContactView'    

// add this object to routes array
{
      path: '/contact',
      name: 'ContactView',
      component: ContactView
}

Above I added a contact route. Going to this route will display the ContactView component. Be sure to import your component before using it.

http://localhost:8080/#/contact – Route

Components

You can place your components wherever you want once you import them accordingly. However the template I used had a components folder so all my components when there. The main component is the BlackBoard.vue but I have other views as well. Well actually App.vue is the main component. The other components are passed through it using the router component.

Declarative Rendering

Vue does this thing where you can declarative render data to the DOM using straightforward template syntax. So for the blackboard Vue component I have the message variable which is declared in the data function of my component.

// BlackBoard.vue - template tag
<template>
  <div>
    {{ message }}
  </div>
</template>
// Blackboard.vue - script tag  
<script>

export default {
  name: 'BlackBoard',
  data: function () {
     return {
       message: "hello world",
     }
   },
}

</script>

So when the view loads the div element will display the message “hello world”. For components you have to return the data has a function as show above. However if it was just the app instance you can do the following.

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

If I change the variable message it would reflect in the view. So I could update my page that easily without refreshing the page. How I would change it you will see shortly.

Binding Attributes

v-bind

Using v-bind we add dynamic styles to the div. To learn more about v-bind check out the docs here.

// blackboard.vue - template sec

<div class="board" v-bind:style="{backgroundColor: bgColor, fontSize:realTextSize, color:ftColor}">
          {{ message }}
</div>

The above code shows the v-bind on styles attribute. It takes an object with some keys and values. Vue changes the hyphenated CSS styles to camel case so background-color become backgroundColor. The values are variables namely bgColor and ftColor. Note that realTextSize is special we will come back to that. With the v-bind on styles when can now dynamically change the background color and the text color, all we have to do is change the variables bgColor and ftColor.

// blackboard.vue  - script sec 

export default {
  name: 'BlackBoard',
  components : {AboutView},
  data: function () {
    return {
      pageCount:1,
      message: "hello world", // default message
      isActive : true,
      bgColor : "#fff",  // change to change background
      ftColor : "#000", // change to change text color
      textSize : 15,
      pages : {}
    }
  },

v-on

The v-on attaches an event listener to the element. The event type is denoted by the argument. The expression can be a method name, an inline statement, or omitted if there are modifiers present.

The short hand symbol is the at ( @ ) sign. I use the modifier a lot for my buttons section. The buttons section is show below.

<div class="board-tools">
        <button id="btn1" @click="changeColour()">Black/White</button>
        <button id="btn2" @click="changeSizeUp()">+ Size</button>
        <button id="btn3" @click="changeSizeDown()">- Size</button>
        <button @click="addPage()">Save Page</button>
        <button @click="newPage()">New Page</button>
        <button @click="goToUrl('about')">Learn More</button>
</div>

From the above I have a few functions attached using the on click v-on command. I used the short hand version but if I wanted to attach a on click listener using the normal way it would look something like this.

Buttons section showing action buttons
  <button v-on:click="addPage()">Save Page</button>

But the @ sign is much cleaner. So where do all these methods exists. That’s a good question.

v-model

You can use the v-model directive to create two-way data bindings on form input, textarea, and select elements. It automatically picks the correct way to update the element based on the input type. Although a bit magical, v-model is essentially syntax sugar for updating data on user input events, plus special care for some edge cases.

https://vuejs.org/v2/guide/forms.html

The input that I add the v-model directive to was the message box that I typed the information to go on the blackboard.

Enter text input

I added the v-model here so that I could modify the message attribute dynamically. Whatever the users enters in the text field the message attribute will be updated.

 <input v-model="message" type="text" placeholder="Enter text">

Remember the main view of the blackboard has the message attribute inside of it. So the text changes in the main view.

<div class="board">
   {{ message }}
</div>
text input with main div output

v-for

We can use the v-for directive to render a list of items based on an array. The v-for directive requires a special syntax in the form of item in items, where items is the source data array and item is an alias for the array element being iterated on:

https://vuejs.org/v2/guide/list.html#Mapping-an-Array-to-Elements-with-v-for

I used this directive to display the pages section. Once you save a page it is added to the pages section. You can click on a page item and it will display the content of that page in the main view.

Save and Render pages

My layout for the pages section is show below. We have a variable pageCount that changes once a page is added.

  <div class="board-pages">
          <h3>Pages ({{pageCount -1}})</h3>
          <ul class="pages-list">
            <li v-for="item in pages" :key="item.id" >
              <a class="page-item" @click="goToPage(item.id)">{{ item.message }}</a>
            </li>
          </ul>
        </div>

We loop through the pages object and retrieves an item, we pass the item.id to the goToPage function to display the data on screen.

The pages variable is an object created in the data function in the script section of the Vue component. The addPage function shows how a page is added to the pages object.

// snippets from blackboard.vue file
  
pages : {}  // object declared in data() {}

// add a page to the pages object
   addPage () {
      this.pages[this.pageCount] = {
        "id" : this.pageCount,
        "message":this.message
      }
      this.message = "";
      this.pageCount += 1;
    },

The list rendering prefers that each item as a unique id so we create a list of objects with a unique key id. The unique id is the pageCount variable and then we add the message as well. After we add a page to the pages object we clear the message variable and updates the pageCount variable.

When we click on a page item we show the page data.

<a class="page-item" @click="goToPage(item.id)">{{ item.message }}</a>

The goToPage function simply gets the message from the pages object and sets the message variable to it.

// method from methods object in blackboard.vue file

goToPage ( id ) {
      var page = this.pages[id];
      this.message = page.message;
},

Methods

When exporting your Vue component one of the objects that can be added is a methods object. This object takes a list of functions you created that can be accessed in the template Vue. When you add on click directives you can point the event to a function you created in the methods object of your component.

// blackboard.vue - script sec

export default {
  name: 'BlackBoard',
  data: function () {
    return {
      message: "hello world"
    }
  },
  methods: {
    changeColour () {
      var colors = ["#fff","#000"];
      if(this.bgColor === colors[0]){
        this.bgColor = colors[1];
        this.ftColor = colors[0];
      }else{
        this.bgColor = colors[0];
        this.ftColor = colors[1];
      }
      console.log("changing to false");
    },

Above I show the changeColor function. It toggles the screen display between black and white. If the background is white the text color is black and vice versa. In the template section this method is accessed easily

<button id="btn1" @click="changeColour()">Black/White</button>

Computed Properties

You can have inline computed properties since Vue brackets allow you to add some JavaScript functions in them. But for better usability you can return a computed object in your Vue component.

// blackboard.vue - script sec
  
computed: {
    // a computed getter
    realTextSize: function () {
      // `this` points to the vm instance
      return this.textSize + "px";
    },
  }

Here I used realTextSize as a computed property. In order to increase the text size I had to format the integer textSize and add the px. You can see how the computed property is used below. Once the textSize changes the realTextSize gets updated and the fontSize gets updated because of the v-bind property on style for the div. Its all pretty neat stuff. The textSize variable is declared in the data object.

// blackboard.vue - template sec

<div class="board" v-bind:style="{backgroundColor: bgColor, fontSize:realTextSize, color:ftColor}">
          {{ message }}
</div>

Child Components

To test the child components features of Vue I added an about view in my blackboard.vue file. Once you import the component and add it to the components object in your Vue instance you can use the component has a tag in the template.

// blackboard.vue file

<AboutView/> // rendered from this tag

<script>
import AboutView from './AboutView.vue'; // import

export default {
  name: 'BlackBoard',
  components : {AboutView}, // add imported component
}

</script>

Its that simple. Now whatever I put in the AboutView.vue file I can see in the blackboard.vue file. You can pass data between components as well but I didn’t research that any further.

So far we have being using single file components but from the Vue documents I just wanted to try one of the component instances. Their documentation has this component below.

// main.js file

Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

Once a component is created like this you can access in you app views. So in the blackboard.vue file all I had to do was add the component as a tag and it worked perfectly.

 <button-counter></button-counter>

Conclusion

This is by far a pretty small demo for using Vue. But it shows its power and what you can accomplish with it. It has really power in interactivity. When using it think about dynamic applications. One’s that will require user feedback, live instant user feedback. Don’t just use Vue to build your simple static applications. Consider its power and use it accordingly. Well actually Vue is great for mockups as well. Guess its all up to you.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s