Getting started with Vue.js by making a comment app

In this post, we are going to build a simple commenting system using Vue.js v2 and for the backend we will use PHP, SQLite. The idea it to get your feet wet with Vue.js, Its not going to be complete application to use in production but at the end of this post you will be confident enough to start writing in Vue.js

So without further ado lets get into it.

What we will be creating?

Comment app using Vue.js

Source Code Demo

Before jumping into the code let’s first plan a little. so what we need here, according to the requirement we need a Form and list of Comments. Breaking down it further it turns out we will need three major component.

  • App
  • CommentForm
  • Comment

Vue-CLI

Vue has a nice set of tools and one of them is Vue-CLI, to scaffold your app in seconds. If you don’t have it, run npm install -g vue-cli to install.

Now that’s out of the way, let’s create our project, Vue-CLI comes with a bunch of template for this I am using webpack-simple one to keep things simple.

vue init webpack-simple my-project

Webpack Simple template will give us Webpack + vue-loader setup, cd into generated project and run npm install or yarn install.

Folder Structure

Below is the folder structure webpack-simple template will give us.

Folder Structure

Thanks to Vue-CLI we have main.js and a component, App.vue generated for us, let’s see whats in main.js.

import Vue from 'vue'
import App from './App.vue'

new Vue({
  el: '#app',
  render: h => h(App)
})

As you can see in main.js we are importing Vue library and an App Component, Vue.js doesn’t require any special configuration, so it should work straight out of the box, to start the app we have to create a global Vue instance, we just need an element on HTML page which will become the root of our app, you can see it in index.html file, we have a div with #app id and we are loading bundled script file below that,  we are rendering our App Component in it.

Now in terminal run npm run dev to begin the build process and launch the app in the browser, If everything goes well, you should see this screen.

Default landing page for template

Create Vue Components

So whats a component? Components are custom elements that Vue.js’ compiler would attach specified behaviour to. They help you extend basic HTML elements to encapsulate reusable code, for example, anything which you might need in more than one place in your app UI, its best idea to make it a component.

Let’s create our first component.

CommentForm Component

As you might have guessed its a form to post comments, it can be used at many places in any app.

Create a file named src/ComponentForm.vue and add these code, I will explain it next.

<template>
	<div class="comment-box" id="comment">
        <div class="loader" v-show="loading" >
          <span class="spinner"></span>  
        </div>
        
        <form action="" method="post" @submit.prevent="submit">
          <textarea v-model="data.message" class="input-message" name="message" id="message" rows="3" placeholder="Your comment..." required></textarea>
          <input v-model="data.name" class="input-name" type="text" name="name" placeholder="Your Name" required>
          <input :disabled="loading" type="submit" value="Comment">
        </form>
    </div>
</template>

<script>
export default {

    data() {
      return {
        loading: false,
        data: {}
      }
    },

    methods: {
      submit() {
        
      }
    }

}
</script>

 

This is the structure of a component, we have our <template> block which contains the HTML template for the form and a <script> block which contains our Component declaration.

The main thing to notice is that data(), it will return all the data for this components scope, it has loading: false which you see in template its bounded with v-show directive, which will initially hide the loader part of HTML since its value is false. and comment: {} which is our comment bound to form inputs.

Below data, is the methods property, which currently has submit handler function, check the @submit.prevent=“submit” on form tag, so on form submit, submit() method will be called. submit.prevent is an event modifier, it prevents the default action of form submit, its similar to event.preventDefault() in javascript.

Vue.js has some other modifiers for events also, please check them out.

Now lets complete this component, we need to post the form, for that I am going to use vue-resource library which is an HTTP library to make consumption of APIs easy.

npm install vue-resource

It will pull the dependency, now open main.js and add below lines before Vue instance to configure it.

import VueResource from 'vue-resource'

Vue.use(VueResource);

// send the request as application/x-www-form-urlencoded
Vue.http.options.emulateJSON = true;

 

and in CommentForm.vue lets completed the submit method

methods: {
      submit() {
        this.loading = true;

        // Save Comment
        this.$http.post('http://localhost:3434', this.data).then((response) => {
          // success callback

          // fire event for comment
          this.$emit('commented', response.data);  

          // Clear the message
          this.data.message = "";

          this.loading = false;
        }, (response) => {
          // error callback
          this.loading = false;
        });
      }
}

Its pretty straight forward, we are posting comment and on success we are emiting event with the response data. Form component is ready at this moment lets create Comment component.

Comment Component

Create component file src/Comment.vue and add these code.

<template>
	<li transition="slide">
      <div class="profile"><img :src="avatar" alt=""></div>
      <div class="msg">
        <div class="msg-body">
          <p class="name">{{ comment.name }} <span class="date">{{ comment.time }}</span></p>
          <p v-html="comment.message"></p>
        </div>
      </div>
    </li>
</template>

<script>
export default {

	props: ['comment'],

	computed: {
		avatar: function () {
			return 'https://api.adorable.io/avatars/48/' + this.comment.name.toString().toLowerCase().trim().replace(/[\s\W-]+/g, '-')  + '@adorable.io.png'
		}
	}

}
</script>

Similar, just two new things, this component has props: [‘comment’] property, this is used to accept data in from outside of component scope. So we will have the comment data object passed from outside.

Another one is computed property, this I have used since I wanted to create an avatar URL based on the name from comments, for avatar images I picked https://api.adorable.io api, so what avatar function does just slugify the name and append it to API call so we can have a different avatar for different names. This avatar property is bounded to <img :src=“avatar”>.

App Component

This is the main component which will be working as the glue between our comment list and form since we already have an App Component we can use it, but first, delete everything in src/App.vue and type below code.

<template>
  <div id="app">
    <div class="brand">
        <img src="src/assets/logo.png" width="100" alt="Vue logo">  

        <div class="great-question">
          <h3>{{ question }}<span>?</span></h3>
          <a href="#comment" class="btn-primary">Comment</a>
        </div>

        <div class="comment-list">
        <span v-show="loading" class="spinner"></span>
          <ul>
              <comment v-for="comment in comments" :comment="comment"></comment>
          </ul>
        </div>

        <comment-form v-on:commented="updateComment"></comment-form>
    </div>
  </div>
</template>

<script>

import Comment from './Comment.vue'
import CommentForm from './CommentForm.vue'

export default {

    data () {
      return {
        question: 'What you think about Vue.js',
        comments: [],
        loading: false
      }
    },

    components: {
      Comment,
      CommentForm
    },

    created () {
        this.loading = true;

        // Fetch the comments 
        this.$http.get('http://localhost:3434').then((response) => {
            // success callback
            this.comments = response.data;
            this.loading = false;
        }, (response) => {
            // error callback
            console.error(response);
            this.loading = false;
        });
    },

    methods: {
      updateComment (comment) {
        this.comments.push(comment);
      } 
    }

}
</script>

This is a big one right! don’t worry, the idea is similar, in script tag we are importing both Comment & CommentForm components, we need to tell this component that we are using child component so we add them on components properly. see in template we have <comment v-for="comment in comments" :comment="comment"></comment> and <comment-form v-on:commented="updateComment"></comment-form> component.

We see one new created() function on this component, components have their lifecycle hooks, created is one of them. its get called when the component is being created. In here I am fetching the list of comment from API & storing them on data property comments.

The template is using v-for directive to loop through the comments array and passing comment object to comment property which we have asked in Comment component.

In methods we have a handler for commented event which we emitted from comment form component when we post a comment, here we just pushing this comment to comments array.

Now our commenting system has competed, check the browser you should see the comment form, try posting one comment. I haven’t covered backend and CSS part of this app since it was not the main focus of this post. You can get the code for API which uses simple PHP and SQLite.

To run the backend API, cd into api folder and it has index.php and comm.sqlite db file, while in this folder run php -S localhost:3434, it will fire up a PHP dev server.

I hope you have learned something and I will love to hear your thoughts on this, please comment and let me know.

Source Code Demo