Codementor Events

Building an App with Vue.js

Published Jun 16, 2017
Building an App with Vue.js

It's a pleasure to welcome you to what I hope will be a series of tutorials on how to build a real life application using Vue.js as the front-end.

Here is the REPO

For better productivity of the code, I will use:

  • Stylus for the CSS
  • Pug (before Jade) for the HTML
  • ES6 for the code

For the initial project structure, we are going to use Vue-cli

Vue-cli offers a simple way to create Vue-based apps. It’s just great and simple, and if you would like a step-by-step guide on how to use Vue-loader and the other tools, we can write a 0.1 tutorial on this, but for now Vue-cli will accomplish what we want.

Installing Vue-cli

Simply run npm install -g vue-cli.

Creating the Project

This project lives inside this repo.

Let's start by creating a very simple app, with no tests, no routing, no eslint, just the base. The console should look like:

? Project name vue-app
? Project description VueJS Playground app
? Author ethaan <your.name@gmail.com>
? Vue build standalone
? Install vue-router? No
? Use ESLint to lint your code? No
? Setup unit tests with Karma + Mocha? No
? Setup e2e tests with Nightwatch? No

Folder Structure

Screen Shot 2017-06-08 at 11.39.16 PM.png

Where...

  • build is where the build options for {dev | prod} live
  • config contains webpack-related things and env variables for development
  • src is where all the action will happen; this is where our front-end app will live
  • static has any images or static files
  • ...others babel, readme, editor, index.html and other files

Starting the App

We are going to use yarn because we are hipsters and fancy people (but you can use npm if you want)

yarn && yarn dev

If everything runs OK, a new browser should open and the console should look like:

 DONE  Compiled successfully in 3103ms                                             
> Listening at http://localhost:8080

Writing our First Vue.js Component

First, start by removing the default Hello.vue component by running rm src/components/hello.vue

and make sure src/App.vue looks like the following:

<template></template>
 
<script>
export default {}
</script>
 
<style></style>

About App.vue

Vue, like React, is pretty simple in how it’s initially rendered into our HTML. If you take a look into src/main.js, ignoring the comments, it just contains 8 lines of code (if you came from React World, you can think of this as

ReactDOM.render(<App />,document.getElementById('root'));)

Hands-on Work

Okay, enough talk; it’s time to build the first component! Let’s try to build one component that uses the most of the Components API.

Component Structure

All the vue components we are going to write use the following structure:

HTML
JS
CSS

This will make our life easier, so you don't need to open 3 different files to make a simple change; everything lives in the same file. Awesome, right?

Let’s start by creating our CodeMentorComponent.vue inside the src folder
and copy/pasting the structure of the App.vue component.

Adding Pug (jade)
Since Vue.js is awesome, simply adding lang="jade" into the template tag like

<template lang="jade"></template>

will make our Vue component use Jade. You can read more at Single File Components.

This will leave us with a CodeMentorComponent looking like this:

<template lang="jade">
.code-mentor-component
  h1 Hello Codementor People
</template>

Pretty simple, right? But how do we use it on the App.vue component? The answer is components:{}. Here, we are going to use the components namespace.

This basically tells the component what components to use, so go into App.vue and import the component and add the components object:

<script>
import CodeMentorComponent from './components/CodeMentorComponent';
 
export default {
  components: { CodeMentorComponent }
}
</script>

This will then use the Component CodeMentorComponent:

<template lang="jade">
  div
    CodeMentorComponent
</template>

If everything runs OK, you should see this on the screen:

Screen Shot 2017-06-09 at 12.22.33 AM.png

Now, let’s start doing some more interesting stuff.

We’ll start by creating a simple counter inside the CodeMentorComponent component, for which we are going to use the data method. In short this is a function that returns an {Object}. Our <script></script> should look like this:

<script>
export default {
  data() {
    return {
      counter: 0
    }
  }
}
</script>

And it can be called inside the <template></template>:

p The counter value is: {{ counter }}

Still pretty simple. Let’s create a FormButton component inside src/forms/Button.vue. It should look like this:

<template lang="jade">
  button(
    @click="$emit('click')"
  ) {{ buttonText }}
</template>
 
<script>
export default {
  props: {
    buttonText: {
      type: String,
      default: 'Click',
    }
  }
}
</script>

At this point, our button component will look pretty simple, but let’s take a deeper look into the new things added here, $emit, @click and slot.

@click is just a shorthand for v-on directive.

$emit basically triggers an event that you previously attached to the component, so we can start thinking about how this component will be called, maybe something like Button(@click="onClick") (but don’t worry about this code yet).

props This object represents what props the component will take, in this case buttonText, which is expected to be of the type String. Also, we are using default to be click. There are other options to pass into the props and ways to call them; you can check them out here.

Let’s continue...

Go ahead and import the button component inside CodeMentorComponent, like so:

import FormButton from './forms/Button';, and don’t forget to add it into the components object:

components: { FormButton },,

Next, call it on the <template></template>:

FormButton(buttonText="Sum")

That’s how you pass the props, but what about computed values, or values inside the data object? Simple, you can use the v-bind directive:

First, our data object now looks like this:

  data() {
    return {
      counter: 0,
      sumButtonText: 'sum',
    }
  }

Then our FormButton component is called:

FormButton(v-bind:buttonText="sumButtonText")

Nows let’s start some reactive work. We want to sum the value of counter every time the button is clicked.

To accomplish that, we need to take in place methods:

  methods: {
    onClick() {
      this.counter = this.counter + 1;
    }
  },

Pretty simple, but wait, our button still doesn’t do anything! For that, we need to attach the method as an event on the component, using the shorthand @, like:

FormButton(@click="onClick" v-bind:buttonText="sumButtonText")

And BAM, we got a reactive counter. Super cool, right? We are not done yet; we want to play with Vue, but what else we can do? We still have some basic directives to use.

Let’s try some fun things: let’s change the color of the counter depending on the counter. We’ll call that levels, so let’s go and create the following object on the <script></script>.

const COLORS_BY_LEVEL = {
  normal: 'blue',
  danger: 'red',
  warning: 'yellow',
};

For that, we are going to integrate computed property to our component, and this will look like:

  computed: {
    levelColor() {
      const { counter } = this;
      let level = 'normal';
      if (counter > 3 && counter <= 8) level = 'warning';
      else if (counter >= 9) level = 'danger';
      return COLORS_BY_LEVEL[level];
    }
  },

Then, just call it on the span:

p(v-bind:style={color: levelColor}) The counter Value is: {{ counter }}

Here we are using inline-style, but you can also do v-bin:class="klass", and have a klass computed method that returns the class name you want.

Instead of a computed property, we can define the same function as a method instead. As far as the end result, the two approaches are exactly the same. However, the difference is that computed properties are cached based on their dependencies. A computed property will only re-evaluate when some of its dependencies have changed. This means that, as long as {counter} has not changed, multiple access to the {levelColor} computed property will immediately return the previously computed result without having to run the function again.

From Computed Properties and Watchers - Basic Example.

This is awesome, right? Let’s keep playing. We’ll now add a second rest button, and call it like so:

FormButton(@click="onClick" buttonText="rest")

BUT WHAT? the two buttons do exactly the same thing! Does this means I need to create two different methods just to sum || rest? No — you can call methods as normal function and pass parameters. Let’s get a little fancy.

We’ll change our buttons to look like this:

  FormButton(@click="onClick({ action: 'sum'})" v-bind:buttonText="sumButtonText")
  FormButton(@click="onClick({ action: 'rest'})" buttonText="rest")

and our onClick method to look like the following:

  methods: {
    onClick({ action }) {
      this.counter = action === 'sum' ? this.counter + 1 : this.counter - 1;
    }
  },

And we still have more stuff to do!

Let’s add more things into the component, and use the v-if directive more here.

We’ll use the same logic for levels; let’s show a custom message (we can do this more cleanly by using a computed method and then just doing MESSAGE_BY_LEVEL[lebel], but let’s use some ifs).

Just add the following:

  p(v-if="counter <= 3") HEY ALL OK
  p(v-else-if="counter > 3 && counter <= 8") HEY HEY HEY! EASY
  p(v-else) AAAAAAAA

There is another directive called v-show, and you can see the comparison here.

Now you have some ifs inside the components, reactive and working great.

Let’s use the last directive, v-for more here.

Let’s. add an exclamation (!) for every positive count.

For this, just add the following:

  .exclamations
    h1(v-for="num in counter") !

and don't forget about the style (let's make them look cool)!

<style lang="stylus">
.exclamations
  display: flex
</style>
 

Stylus loaders came already supported by Vue-cli; for more details, take a look on vue-app/build/utils.js.

Vue, you are so easy.

This is it for today! Hope that was enjoyable 😃 Don’t worry, more will come. We will be integrating GraphQL, express, Routing, Vuex (aka Redux for vue), and more interesting stuff. “We want to create {insertIdeaHere}”, you say — no problem! We can do whatever you all want. If you are itching to start building more cool stuff, take a look on Ethaan-Vuexpresso.

If you liked this post and want more like it, post in the comments what you’d like to see next!

div(v-if="peopleWantsMore)
 ul
  li(v-for="posts on ethanVuePosts)
   Post(v-bind:post="post"
Discover and read more posts from Ethan Escareño Rosano
get started
post comments2Replies
David
7 years ago

very helpful!

Ethan Escareño Rosano
7 years ago

Gracias David