BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Getting Started with Vue.js

Getting Started with Vue.js

Bookmarks

Key Takeaways

  • Vue.js works well for the teams that have to rewrite old codebases one step at a time.
  • Vue CLI is the tooling for the Vue ecosystem and ensures that the various build tools work well together.
  • Axios is a standard library for making HTTP requests.
  • Bulma is a UI framework that works without dependencies.
  • Cloudinary is a tool used for smart image manipulation.
  • Amazon S3 (Amazon Simple Storage Solution) service is a solid choice for hosting simple web applications.

TL;DR

In this rather long tutorial, you'll learn how to use Vue CLI to build a Vue.js 2 application for searching and displaying Giphy images, and then manipulating/transforming them by using the Cloudinary API. Also, you'll learn how to deploy this application to AWS.

__ We’ll be using Vue.js 2, which will be referred to as Vue.js in the rest of this post.

Introduction

Due to Vue's progressive and flexible nature, it's ideally suited for teams that have to rewrite old codebases one step at a time.

If you want to check out some detailed comparisons between popular frontend frameworks, here are three great posts:

I tend to answer general 'framework war' kind of questions in the following way:

  • Stop with the ‘analysis paralysis’ discussion.
  • Do your research, pick a framework—any framework—that the community is using, use it, and see how far it gets you.
  • It isn’t worthwhile to discuss X being slow or Y being better until you try it for your specific use case and preference.

In this post, we'll use Vue.js and Cloudinary to apply some image manipulation techniques to the images that we'll fetch using the Giphy API in the demo application that we'll build. In the end, we'll use AWS to host the application. This is a hands-on from start to finish tutorial with all steps outlined.

Demo app

You can fork the complete source code on GitHub, or you can see the finished app in action.

Prerequisites

Make sure that you have the following tools installed:

Vue CLI

As detailed in the Vue CLI README:

Vue CLI aims to be the standard tooling baseline for the Vue ecosystem. It ensures the various build tools work smoothly together with sensible defaults so you can focus on writing your app instead of spending days wrangling with configurations. At the same time, it still offers the flexibility to tweak the config of each tool without the need for ejecting.

To install vue-cli, run:

npm install -g @vue/cli

To confirm that the installation ran successfully, run:

vue --help

__ For reference, the version of the CLI (vue --version) used in this tutorial is 3.0.0-rc.3.

Starting a new app with Vue CLI

We'll call our app image-search-manipulator. Let's start a new app using vue-cli:

vue create image-search-manipulator

After this command finishes, let's cd into the project and run it:

cd image-search-manipulator
npm run dev

 

You should get a console log similar to:

DONE  Compiled successfully in 7859ms

Your application is running here: http://localhost:8080

 

You should see the following page in your browser if you visit this link: http://localhost:8080.

Folder structure

Now, let's open this project in the editor of your choice (I'm using Visual Studio Code), and you should see something like this:

Here we'll only focus on the src folder. The contents of that folder should be something like this:

Adding content

If you search for the string Welcome to Your Vue.js App, you'll see the string is within the HelloWorld.vue file. A *.vue file is a custom file format that uses HTML-like syntax to describe a Vue component. This file contains the following (... is used for brevity in the listing below):

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h2>Essential Links</h2>
    <ul>
      ...
    </ul>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

Without knowing anything about Vue.js, we can see where we would change the Welcome to Your Vue.js App text. So, let's change that to Welcome to Image Search Manipulator. While you do that, also remove the contents of the style tag, as we don’t need it here.

Adding input and a button

Our basic application should have one input field and one button.

To do this, add the following code to the HelloWorld.vue file in the template:

<input name="search">

<button>Search</button>

 

The template element of HelloWorld.vue should look like this now:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    
    <input name="search">

    <button>Search</button>
  </div>
</template>

Actions

Having a simple search input field and a button does not do much on their own. We want to click the button, and we want to output something to the console just to verify that the button is working correctly.

This is how we define a function that will handle the button click in Vue:

<button @click="performSearch">Search</button>

Vue applications often contain an equivalent alternative shorthand syntax:

<button v-on:click="performSearch">Search</button>

Within the browser's developer tools, you'll observe an error such as:

webpack-internal:///./node_modules/vue/dist/vue.esm.js:592 [Vue warn]: Property or method "performSearch" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option or for class-based components, by initializing the property. See https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.

found in

---> <HelloWorld> at src/components/HelloWorld.vue
       <App> at src/App.vue
         <Root>

This error occurs because we haven't defined the performSearch function. In the HelloWorld.vue file, add the following function definition inside the script tag, methods object property:

<script>
export default {
  name: "HelloWorld",
  data() {
    return {
      msg: "Welcome to Image Search Manipulator"
    };
  },
  methods: {
    performSearch: function() {
      console.log("clicked");
    }
  }
};
</script>

We’ve now defined the performSearch function, which doesn't accept any parameters and has no return value.

Taking input

To print the string that was typed in the input field to the console, we first need to add a new attribute to the input field:

<input name="search" v-model="searchTerm">

The v-model instructs Vue.js to bind the input to the new searchTerm variable, such that whenever the input text updates, the value of searchTerm also gets updated.You can learn more about v-model and other form input bindings in the Vue documentation.

Finally, change the performSearch function in order to log the value to the console:

performSearch: function() {
    console.log(this.searchTerm);
}

Giphy search API

With our example application, we now want to connect our search fields to an API call to return images. Giphy provides a search API. We need to determine the request parameters needed to search Giphy's database. If we open the search API link, we determine the format of the service: 

In the next section, we'll cover retrieving this data from within our app.

Vue.js HTTP requests

There are several ways to send HTTP requests in Vue.js. For additional options, check out this post about making AJAX calls in Vue.js.

In this tutorial, we’ll use Axios, a popular JavaScript library for making HTTP requests. It's an HTTP client that makes use of the modern Promises API by default (instead of JavaScript callbacks) and runs on both the client and the server (Node.js). One feature that it has over the native .fetch() function is that it performs automatic transforms of JSON data.

In your Terminal/Command prompt enter the following command to install Axios via npm:

npm install axios --save

Import Axios into the HelloWorld.vue file just after the opening script tag:

import axios from "axios";

The performSearch function should now look like this:

const link = "http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&q=";
const apiLink = link + this.searchTerm;

axios
  .get(apiLink)
    .then(response => {
      console.log(response);
    })
    .catch(error => {
        console.log(error);
    });

For reference, the contents of the HelloWorld.vue file should now be:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    
    <input name="search" v-model="searchTerm">

    <button @click="performSearch">Search</button>
  </div>
</template>

<script>
import axios from "axios";

export default {
  name: "HelloWorld",
  data() {
    return {
      msg: "Welcome to Image Search Manipulator"
    };
  },
  methods: {
    performSearch: function() {
      const link = "http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&q=";
      var apiLink = link + this.searchTerm;

      axios
        .get(apiLink)
        .then(response => {
          console.log(response);
        })
        .catch(error => {
          console.log(error);
        });
    }
  }
};
</script>

__ When you're testing, you may also want to limit the number of images that you get from Giphy. To do that, pass limit=5 in the link like this: http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=5&q=

Now, if you run the app, enter something in the search box, and click the search button, you'll see something like this in your console log:

The response object is returned, and in its data property there are 25 objects, which hold information about the images that we want to show in our app.

To show an image, we need to drill down on the object images, then fixed_height_still, and finally on the url property.

Also, we don't want to just show one image but all of the images. We'll use the v-for directive for that:

<img v-for="i in images" :key="i.id" :src="i.images.fixed_height_still.url">

The v-for directive is used to render a list of items based on an array and it 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.

For reference, here's the full listing of the HelloWorld.vue file:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    
    <input name="search" v-model="searchTerm">

    <button @click="performSearch">Search</button>

    <div>
        <img v-for="i in images" :key="i.id" :src="i.images.fixed_height_still.url">
    </div>
  </div>
</template>

<script>
import axios from "axios";

export default {
  name: "HelloWorld",
  data() {
    return {
      msg: "Welcome to Image Search Manipulator",
      searchTerm: "dogs",
      images: []
    };
  },
  methods: {
    performSearch: function() {
      const link =
        "http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=5&q=";
      var apiLink = link + this.searchTerm;

      axios
        .get(apiLink)
        .then(response => {
          this.images = response.data.data;
        })
        .catch(error => {
          console.log(error);
        });
    }
  }
};
</script>

At this point, if we take a look at the app and search for 'coding', we'll get this:

Making the app look pretty with Bulma

Even though our example application functions as expected, the results do not look great.

Bulma is an open source CSS framework based on Flexbox. Bulma is similar to Bootstrap with fewer interdependencies, and is just pure CSS without adding JavaScript.

First, let's install Bulma:

yarn add bulma if you're using Yarn, or npm install bulma if you're using NPM.

Add bulma to the App.vue file, just after the import HelloWorld from "./components/HelloWorld"; line, like this:

import "../node_modules/bulma/css/bulma.css";
 

Here's the template from the HelloWorld.vue file after adding Bulma classes to it:

<template>
    <div class="container">
        <h1 class="title">{{ msg }}</h1>
    
        <input name="search" class="input" v-model="searchTerm">
        <br><br>
        <button @click="performSearch" class="button is-primary is-large">Search</button>

        <div class="myContent">
            <img v-for="i in images" :key="i.id" :src="i.images.fixed_height_still.url">
        </div>
    </div>
</template>

Here's a recap of what we did:

  • Added the class container to the first div tag
  • Added the class title to the h1 tag
  • Added the class input to the input tag
  • Added the following classes to the button: button is-primary is-large

For demonstration purposes, the myContent class was added like this:

.myContent {
  padding-top: 20px;
}

And a bit of padding was added around the img tags:

img {
    padding: 5px;
}

The following classes were added to the HelloWorld.vue file like this:

<style>
.myContent {
  padding-top: 20px;
}

img {
  padding: 5px;
}
</style>

With these few quick changes, we now have a better-looking app:

Bulma has many options without the overhead typical of larger CSS frameworks.

Image manipulation

We’ll now use a few techniques to manipulate the images.

Cloudinary is a service offering many image manipulation options.

After you create a Cloudinary account, you need to install Cloudinary:

npm install cloudinary-core --save

For reference, here's the full source code listing and a summary of changes:

  • Added new div <div class="myContent" v-html="manipulatedImages"></div>. Because the manipulatedImages variable will contain HTML, we need to use the v-html directive to show it in the template as such, and not as a string. You can learn more about the v-html directive in the Vue documentation.
  • Imported Cloudinary import cloudinary from "cloudinary-core";
  • Called Cloudinary on each image returned from the Giphy API (starts with const cloudinaryImage = cl):
<template>
    <div class="container">
        <h1 class="title">{{ msg }}</h1>
    
        <input name="search" class="input" v-model="searchTerm">
        <br><br>
        <button @click="performSearch" class="button is-primary is-large">Search</button>

        <div class="myContent">
            <img v-for="i in images" :key="i.id" :src="i.images.fixed_height_still.url">
        </div>

        <div class="myContent" v-html="manipulatedImages"></div>
    </div>
</template>

<script>
import axios from "axios";
import cloudinary from "cloudinary-core";

export default {
  name: "HelloWorld",
  data() {
    return {
      msg: "Welcome to Image Search Manipulator",
      searchTerm: "dogs",
      images: [],
      manipulatedImages: ""
    };
  },
  methods: {
    performSearch: function() {
      var link =
        "https://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=5&q=";
      const apiLink = link + this.searchTerm;

      const cl = new cloudinary.Cloudinary({
        cloud_name: "nikola"
      });

      axios
        .get(apiLink)
        .then(response => {
          this.images = response.data.data;

          this.images.forEach(image => {
            const cloudinaryImage = cl
              .imageTag(image.images.fixed_height_still.url, {
                width: 150,
                height: 150
              })
              .toHtml();

            this.manipulatedImages += cloudinaryImage;
          });
        })
        .catch(error => {
          console.log(error);
        });
    }
  }
};
</script>

<style>
.myContent {
  padding-top: 20px;
}

img {
  padding: 5px;
}
</style>

Deploying to AWS S3

We're going to complete this tutorial by publishing our app on Amazon S3 (Amazon Simple Storage Solution).

First, login (or create an AWS account) to the AWS Console and search for S3:

Familiarize yourself with the interface and create a new AWS S3 bucket. Buckets are places where we may add files. Make sure that, when you create the bucket, you select the Grant public read access to this bucket setting as bucket contents are private by default:

Finally, go into your S3 bucket and enable static website hosting (enter index.html as suggested in the Index document field):

Build it

Run npm run build to build a production version of your app. All production-ready files are going to be placed in the dist folder.

If you get a blank page after you upload the contents of the dist folder to the AWS bucket and visit the website, then check out the solution which basically states that you need to remove the / from the assetsPublicPath in config/index.js:

If you encounter the blank page issue, perform the step above and repeat the npm run build command.

Upload

Upload the entire content of the dist directory to the S3 bucket. This can be done using a simple drag and drop. Make sure you set permissions for the files so that they are globally read accessible:

That's all there is to it! 

You may view my version of the app created in this tutorial.

Backup

An important tip to finish up the tutorial is to backup your work. Several options are available for backing up apps on AWS, including the AWS native solution, and other, third-party solutions that offer added features such as zero downtime. These might not be required for your basic app, but are worth looking into for larger-scale use cases.  

It all depends on your stack—but there are solutions for backing on any stack. For example, on the Azure stack, you could use Azure Backup.

Conclusion

In this tutorial, we learned how to get started with using Vue.js by building an application for searching images via Giphy's API. We prettified our app with the Bulma CSS framework. Then we transformed the images using Cloudinary. Finally, we deployed our app to AWS

Please leave any comments and feedback in the discussion section below and thank you for reading!

About the Author

Gilad David Maayan is a technology writer who has worked with over 150 technology companies including SAP, Oracle, Zend, CheckPoint and Ixia, producing technical and thought leadership content that elucidates technical solutions for developers and IT leadership.

Rate this Article

Adoption
Style

BT