DemoCode

Vue.js 2 is great for building powerful single-page web applications. In the following we’re going to build a real-world Vue.js 2 application which makes use of an external REST API from Newsapi.org. By using this free API we’re able to retrieve latest news articles from popular news sources and blogs. To access this external API we’re going to use the vue-resource packages which provides a service for making web requests and handle responses.

What We’re Going To Build

In the following screenshot you can see what the application we’re going to build looks like:

The user is able to select a news source from a drop-down list. Having selected a news source the news article list at the bottom is automatically updated with the latest news articles from this source. Clicking on the headlines takes the user to the original website of the article.

Project Setup

The easiest way to initiate a new Vue.js 2 project is by using Vue CLI. On the command line just execute the follwing command:

$ vue init webpack vuenews

If you haven’t installed Vue CLI before on your system you can do so by using the following command first:

$ npm install -g vue-cli

This makes sure that Vue CLI is installed globally in your system and that the command vue is available.

Having initialized the application you need to change into the new project directory vuenews and execute the following command to download and install all standard project dependencies:

$ npm install

After the installation has been completed successfully you can start the development web server via:

$ npm run dev

Include Bootstrap

In index.html include Bootstrap (and jQuery) from CDN:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>vuejs-news-01</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> <script src="https://code.jquery.com/jquery-3.1.1.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> </body> </html>

We’ll use the Bootstrap CSS classes for styling the user interface of our application.

Adding vue-resource To The Project

The vue-resource plugin for Vue.js provides services for making web requests and handle responses using a XMLHttpRequest or JSONP.

For our sample application we’d like to retrieve data from https://newsapi.org/ which is a simple and easy-to-use API that returns JSON metadata for the headlines currently published on a range of news sources and blogs. By using vue-resource we’re able to use this API within our Vue.js 2 application.

First add vue-resource to the project by using the following command:

$ npm install vue-resource --save

Next, open file main.js and insert the following import statement:

import VueResource from 'vue-resource'

In addition add the following line of code right under the import statements:

Vue.use(VueResource);

Now we’re ready to use vue-resource within our components.

Implementing App Component

Let’s start with the implementation in component App. Open file App.vue and adapt the source code as you can see in the following:

<template> <div class="container" id="app"> <SourceSelection v-on:sourceChanged="sourceChanged"></SourceSelection> <Newslist v-bind:source="source"></Newslist> </div> </template> <script> import Newslist from './components/Newslist' import SourceSelection from './components/SourceSelection' export default { name: 'app', components: { Newslist, SourceSelection }, data () { return { source: "" } }, methods: { sourceChanged: function (source) { this.source = source; } } } </script> <style> #app { padding-top: 20px } </style>

As we would like to split up our application in two components (SourceSelection and Newslist) we first need to add import statements for both components in the script section. For both components new files will be added in the src/components later on.

We also need to add SourceSelection and Newslist to the object which is assigned to the components property of the component’s configuration object.

Next, we can make use of both components within the template code:

<SourceSelection v-on:sourceChanged="sourceChanged"></SourceSelection> <Newslist v-bind:source="source"></Newslist>

By including <SourceSelection> and <Newslist> within the template of App component we make sure that the HTML output which is generated by both componnets is inserted into the main application template and included in the browser output.

For the element <SourceSelection> we’re attaching the event handler method sourceChanged to the event type sourceChanged. This is a custom event of component SourceSelection and will be emitted each time the user selects a new news source. The event handler method is added to the methods object and makes sure that the source property of App component is updated with the new source value from the user selection.

Later on, when implementing the Newslist component we’ll define an input property named source. By using this property we’re able to pass in the current news source which should be used to retrieve and display a list of news headlines in component Newslist.

To make sure that this input property is always up-to-date we’re binding it to the value of the source property of App component by using the v-bind directive.

Implementing SourceSelection Component

Next, let’s implement the SourceSelection component in the new file src/components/SourceSelection.vue. The complete source code is shown in the following listing:

<template> <div class="sourceselection"> <div> <div class="jumbotron"> <h2><span class="glyphicon glyphicon-list-alt"></span> News List</h2> <h4>Select News Source</h4> <select class="form-control" v-on:change="sourceChanged"> <option value="">Please select news source ...</option> <option v-for="source in sources" v-bind:value="source.id">{{source.name}}</option> </select> <div v-if="source"> <h6>{{source.description}}</h6> <a v-bind:href="source.url" class="btn btn-primary" target="_blank">Go To {{source.name}} Website</a> </div> </div> </div> </div> </template> <script> export default { name: 'sourceselection', data () { return { sources: [], source: '' } }, methods: { sourceChanged: function(e) { for (var i=0; i<this.sources.length; i++) { if (this.sources[i].id == e.target.value) { this.source = this.sources[i]; } } this.$emit('sourceChanged', e.target.value); } }, created: function () { this.$http.get('https://newsapi.org/v1/sources?language=en') .then(response => { this.sources = response.data.sources; }); } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>

The template implementation consists of a select from control which gives the user the possibility to choose from a list of news sources. The options which are presented in the list are taken from the sources array. By using the v-for directive in the <option> element we’re able to iterate over the elements of this array and generate an <option> element for each entry.

Furthermore we’re sonnecting the change event of the select element to the sourceChanged event handler by using the v-on directive in the following form: v-on:change="sourceChanged" .

The sourceChanged method is added to the object which is assigned to the methods property of the component’s configuration object. A for loop is used to iterate over the sources array and compare the source id with the id of the source the user has selected. If a match is found the source property of the component is set to this source element. Furthermore the custom component event sourceChange is emitted and the selected source id is passed in as an event parameter. Emitting this custom element each time the user selection of a news source changes is the prerequisite to be able to subscribe to this to this event from outside of the component like we did in App.vue:

<SourceSelection v-on:sourceChanged="sourceChanged"></SourceSelection>

Because we’re using the sources array to get a list of all available news sources we need to make sure that this array is filled when to component is created. The right place to do that is the lifecycle hook created. We we need to add created as a property to the component’s configuration object and then assign a function which contains code to initialize the array.

We’re using the $http object from the vue-resource package to send a GET request to the Newsapi service which returns a list of news sources (https://newsapi.org/v1/sources?language=en). To send a GET reques to this service endpoint the get method of the $http object is used. This method returns a promise so that we can add the then method to attach a function which is executed when the promise is resolved (the response from the GET request is available). The list of sources which is returned from the web service is available in response.data.sources, so that we can these values to this.source.

Implementing Newslist Component

Next, let’s continue with the implementation of Newslist component. Newslist component is included in the template of component App to generate a list of news headline from the news source selected:

<Newslist v-bind:source="source"></Newslist>

For the implementation of Newslist we create a new file Newslist.vue in src/components/ and insert the following code:

<template> <div class="newslist"> <div class="container"> <ul class="media-list"> <li class="media" v-for="article in articles"> <div class="media-left"> <a v-bind:href="article.url" target="_blank"> <img class="media-object" v-bind:src="article.urlToImage"> </a> </div> <div class="media-body"> <h4 class="media-heading"><a v-bind:href="article.url" target="_blank">{{article.title}}</a></h4> <h5><i>by {{article.author}}</i></h5> <p>{{article.description}}</p> </div> </li> </ul> </div> </div> </template> <script> export default { name: 'newslist', props: ['source'], data () { return { articles: [] } }, methods: { updateSource: function (source) { this.$http.get('https://newsapi.org/v1/articles?source=' + source + '&apiKey=[Place your Newsapi.org API key here]') .then(response => { this.articles = response.data.articles; }); } }, created: function () { this.updateSource(this.source); }, watch: { source: function (val) { this.updateSource(val); } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> .media-object { width: 128px; padding: 10px; } .media { border-top: 1px solid lightgray; padding-top: 20px; } </style>

In order to be able to use the source attribute in element <Newslist> we need to define a corresponding component property by using the props property:

props: ['source'],

With that inbound property available, we now have access to the current news source. We are able to use that piece of information to send another GET request to the Newslist API in oder to retrieve latest news items. To make sure that this request is always executed when the source value changes we establish a watcher:

watch: { source: function (val) { this.updateSource(val); } }

This watcher function is executed each time the value of source changes and is calling the method updateSource. The implementation of updateSource is added to the object which is assigned to methods and contains the code which is needed to retrieve the latest news from the service and assign the results to the articles array. In this case the service endpoint is https://newsapi.org/v1/articles. We also need to add two URL parameters to this service endpoint. The news source we would like to use must be added with URL paramater source. Another parameter which needs to be added is apiKey. The value which we need to assign to this parameter is the API key which can be retrieved from the Newsapi website (http://newsapi.org) after registration.

Again, the call of the get method returns a promise, so that we can use the then method to attach a function which is called each when the promise is resolved. In that case we’re assigning the result of the service request (available in response.data.articles) to the array articles.

With that prerequisite we’re able to implement the component’s template and use the articles array within the template to generate the HTML output to display a list of news articles to the user.