REST Countries

19 Nov 2016   No Comments   2599 Views

Some time ago I was googling to find useful and fun APIs. There are hundreds of them available nowadays, you can find popular APIs on Mashape, AnyApi, ProgrammableWeb and other sources, or just by googling. Building clients for public APIs is fun, and a great learning opportunity. I play with these APIs anytime I get a chance. I build simple apps to use the data, learn and compare different technologies and beyond.

I came across RESTCountries which gives you information about countries! Stuff like their native names, capital cities, location and region, bordering countries and a bunch of other useful information. You don't need to signup for an API Key or anything, all endpoints are publicly available. In this post I'm gonna create a little angular app to use this API. I'm using Angular 1.x and a few other libraries that you probably already know: Bootstrap, Font Awesome, nprogressui-router and jQuery.

This project is a good example for angular beginners. You'll see how to create an app, define routes and controllers, talk to a REST API and a few other things. But I'm assuming you have experience with HTML/CSS, JavaScript and Angular fundamentals. The source code is available on github and you can also play with a live fiddle at the end of this post.

 

Create the project

I'm using Visual Studio Code, but you can use the IDE of your choice. Create a folder and inside it a .bowerrc file. Update the config file with this:

{
  "directory": "lib"
}

This tells bower to install all packages inside a folder named lib (defaults to bower_components). Now open a command prompt or bash console inside your project folder and initialize a bower file with the bower init command. After you've created your bower.json file, install the following packages.

bower install jquery bootstrap angular nprogress font-awesome angular-ui-router --save-dev

If all goes well, you should now have a project like this:

Now create three folders for JavaScript, CSS and HTML Templates. Create a styles.css file, an app.js file and an index.html file. Now we have a structure like the following.

Now we need some JavaScript to create our angular application and a few CSS rules to style our elements. Update your index.html file with the following markup.

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>RESTCountries Angular App</title>

	<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
	<link rel="stylesheet" href="bower_components/nprogress/nprogress.css">
	<link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css">
	<link rel="stylesheet" href="css/styles.css">
</head>

<body ng-app="restcountries">
	<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
		<div class="container">
			<div class="navbar-header">
				<a class="navbar-brand" href="#">REST Countries</a>
			</div>
			<ul class="nav navbar-nav">
				<li><a ui-sref="home">Home</a></li>
				<li><a ui-sref="countries">Countries</a></li>
			</ul>
		</div>
	</nav>

	<div class="container">
		<div ui-view></div>
	</div>

	<script src="lib/angular/angular.js"></script>
	<script src="lib/angular-ui-router/release/angular-ui-router.min.js"></script>
	<script src="lib/jquery/dist/jquery.min.js"></script>
	<script src="lib/bootstrap/dist/js/bootstrap.min.js"></script>
	<script src="lib/nprogress/nprogress.js"></script>
	<script src="js/app.js"></script>
</body>

</html>

Just a standard build to start with, nothing special here. Moving on with the angular app itself. I'm using ui-router for routing. We'll have three routes: HomeCountries and Capital. We'll also have three html files under the templates folder named home.html, countries.html and capital.html. Update your index.html and change its body a little bit.

<body ng-app="restcountries">
	<nav class="navbar navbar-inverse navbar-static-top" role="navigation">
		<div class="container">
			<div class="navbar-header">
				<a class="navbar-brand" href="#">REST Countries</a>
			</div>
			<ul class="nav navbar-nav">
				<li><a ui-sref="home">Home</a></li>
				<li><a ui-sref="countries">Countries</a></li>
			</ul>
		</div>
	</nav>

	<div class="container">
		<div ui-view></div>
	</div>
...

Now we have a simple menu and the container renders whatever ui-router provides. Next update the template files with the following markups.

Home.html markup

<div class="row">
	<div class="col-md-12">
		<div class="jumbotron text-center">
			<h1>REST Counrties</h1>
			<p>Get information about countries via a RESTful API</p>
		</div>

		<div class="content-wrap">
			<form class="form-horizontal" role="form">
				<div class="form-group">
					<div class="col-md-4" style="padding-right: 0">
						<input type="text" class="form-control" placeholder="Enter a capital city" ng-keyup="$event.keyCode == 13 && getCapitalCity()"
							ng-model="capitalCity">
					</div>
					<div class="col-md-2">
						<button type="button" class="btn btn-primary" ng-click="getCapitalCity()">
                                <i class="fa fa-search"></i> GO
                            </button>
					</div>
				</div>
			</form>

			<h1><i class="fa fa-globe"></i> {{countryInfo.name}}</h1>

			<div class="row">
				<div class="col-md-6">
					<p>
						<strong>Capital</strong> {{countryInfo.capital}} <br>
						<strong>Population</strong> {{countryInfo.population | number}} <br>
						<strong>Region</strong> {{countryInfo.region}} <br>
						<strong>Sub-region</strong> {{countryInfo.subregion}} <br>
						<strong>Relevance</strong> {{countryInfo.relevance}} <br>
						<strong>Native Name</strong> {{countryInfo.nativeName}} <br>
					</p>
				</div>
				<div class="col-md-6">
					<p>
						<strong>Numeric Code</strong> {{countryInfo.numericCode}} <br>
						<strong>Alpha 2 Code</strong> {{countryInfo.alpha2Code}} <br>
						<strong>Alpha 3 Code</strong> {{countryInfo.alpha3Code}} <br>
						<strong>Area</strong> {{countryInfo.area}} <br>
						<strong>Demonym</strong> {{countryInfo.demonym}} <br>
						<strong>Gini</strong> {{countryInfo.gini}}
					</p>
				</div>
			</div>

			<div class="row">
				<div class="col-md-4">
					<h4><i class="fa fa-language"></i> Alt Spellings</h4>
					<ul>
						<li ng-repeat="item in countryInfo.altSpellings">{{item}}</li>
					</ul>

					<h4><i class="fa fa-map"></i> Borders</h4>
					<ul>
						<li ng-repeat="item in countryInfo.borders">{{item}}</li>
					</ul>
				</div>
				<div class="col-md-4">
					<h4><i class="fa fa-money"></i> Currencies</h4>
					<ul>
						<li ng-repeat="item in countryInfo.currencies">{{item}}</li>
					</ul>

					<h4><i class="fa fa-language"></i> Languages</h4>
					<ul>
						<li ng-repeat="item in countryInfo.languages">{{item}}</li>
					</ul>

					<h4><i class="fa fa-map-marker"></i> Geolocation</h4>
					<ul>
						<li ng-repeat="item in countryInfo.latlng">{{item}}</li>
					</ul>

					<h4><i class="fa fa-phone"></i> Calling Codes</h4>
					<ul>
						<li ng-repeat="item in countryInfo.callingCodes">{{item}}</li>
					</ul>
				</div>
				<div class="col-md-4">
					<h4><i class="fa fa-clock-o"></i> Time Zones</h4>
					<ul>
						<li ng-repeat="item in countryInfo.timezones">{{item}}</li>
					</ul>

					<h4> Top Level Domain</h4>
					<ul>
						<li ng-repeat="item in countryInfo.topLevelDomain">{{item}}</li>
					</ul>

					<h4><i class="fa fa-language"></i> Translations</h4>
					<ul>
						<li ng-repeat="item in countryInfo.translations">{{item}}</li>
					</ul>
				</div>
			</div>
		</div>
	</div>
</div>

Here's an image of the home template. A simple form to search for Capital Cities and some labels to display the information.

 

Countries.html Markup

<div class="row">
    <div class="col-md-12">
        <h1>Countries <small class="text-muted">{{filtered.length}}</h1>

        <form role="form" class="form-horizontal">
            <div class="form-group">
                <div class="col-md-4">
                    <input type="text" class="form-control" placeholder="Filter by name" ng-model="query" />
                </div>
            </div>
        </form>

        <div ng-repeat="item in countries | filter:query as filtered" class="country-card">
            <h3>
                <a href="" ui-sref="capital({ name: item.capital })">{{item.name}}</a>
            </h3>
            <p>
                <strong>Capital</strong> <br>
                <strong>Population</strong> {{item.population | number}} <br>
                <strong>Region</strong> {{item.region}} <br>
                <strong>Sub-region</strong> {{item.subregion}} <br>
                <strong>Native Name</strong> {{item.nativeName}}
            </p>
        </div>
    </div>
</div>

Here's an image of the countries template.

In the home view you search for a country by its Capital, and the countries view shows the list of all countries. Each country item is linked to another route named capital which shows the information just like the home view.

Capital.html markup

<div class="row">
    <div class="col-md-12">
        <h1><i class="fa fa-globe"></i> {{country.name}}</h1>

        <div class="row">
            <div class="col-md-6">
                <p>
                    <strong>Capital</strong> {{country.capital}} <br>
                    <strong>Population</strong> {{country.population | number}} <br>
                    <strong>Region</strong> {{country.region}} <br>
                    <strong>Sub-region</strong> {{country.subregion}} <br>
                    <strong>Relevance</strong> {{country.relevance}} <br>
                    <strong>Native Name</strong> {{country.nativeName}} <br>
                </p>
            </div>
            <div class="col-md-6">
                <p>
                    <strong>Numeric Code</strong> {{country.numericCode}} <br>
                    <strong>Alpha 2 Code</strong> {{country.alpha2Code}} <br>
                    <strong>Alpha 3 Code</strong> {{country.alpha3Code}} <br>
                    <strong>Area</strong> {{country.area}} <br>
                    <strong>Demonym</strong> {{country.demonym}} <br>
                    <strong>Gini</strong> {{country.gini}}
                </p>
            </div>
        </div>

        <div class="row">
            <div class="col-md-4">
                <h4><i class="fa fa-language"></i> Alt Spellings</h4>
                <ul>
                    <li ng-repeat="item in country.altSpellings">{{item}}</li>
                </ul>

                <h4><i class="fa fa-map"></i> Borders</h4>
                <ul>
                    <li ng-repeat="item in country.borders">{{item}}</li>
                </ul>
            </div>
            <div class="col-md-4">
                <h4><i class="fa fa-money"></i> Currencies</h4>
                <ul>
                    <li ng-repeat="item in country.currencies">{{item}}</li>
                </ul>

                <h4><i class="fa fa-language"></i> Languages</h4>
                <ul>
                    <li ng-repeat="item in country.languages">{{item}}</li>
                </ul>

                <h4><i class="fa fa-map-marker"></i> Geolocation</h4>
                <ul>
                    <li ng-repeat="item in country.latlng">{{item}}</li>
                </ul>

                <h4><i class="fa fa-phone"></i> Calling Codes</h4>
                <ul>
                    <li ng-repeat="item in country.callingCodes">{{item}}</li>
                </ul>
            </div>
            <div class="col-md-4">
                <h4><i class="fa fa-clock-o"></i> Time Zones</h4>
                <ul>
                    <li ng-repeat="item in country.timezones">{{item}}</li>
                </ul>

                <h4> Top Level Domain</h4>
                <ul>
                    <li ng-repeat="item in country.topLevelDomain">{{item}}</li>
                </ul>

                <h4><i class="fa fa-language"></i> Translations</h4>
                <ul>
                    <li ng-repeat="item in country.translations">{{item}}</li>
                </ul>
            </div>
        </div>
    </div>
</div>

The capital template is the same as our home view.  A bunch of labels for displaying information.

 

Building the Angular app

We need three routes: home, countries and capital. Update your app.js file with the following code which defines the application and configures our routes.

var app = angular.module('restCountries', ['ui.router']);
app.config(function($stateProvider, $urlRouterProvider) {
    $stateProvider
        // home state
        .state('home', {
            url: '/home',
            controller: 'homeCtrl',
            templateUrl: 'templates/home.html'
        })

        // countries state
        .state('countries', {
            url: '/countries',
            controller: 'countriesCtrl',
            templateUrl: 'templates/countries.html'
        })

        // countries by capital state
        .state('capital', {
            url: '/capital/:name',
            controller: 'capitalCtrl',
            templateUrl: 'templates/capital.html'
        });

    $urlRouterProvider.otherwise('/home');
});

We've named our application restcountries and imported the ui-router library. Our route definitions are straightforward, we've defined the URLs, controllers and templates for each route. At this point you should be able to launch your application in a browser. I use simplehttpserver to serve static files, its a simple HTTP server available as a node package. You can install this library with the following:

npm install -g simplehttpserver

Then you can open a command prompt in your project's root directory and serve your files by running simplehttpserver.

Now we need to actually communicate with the REST Countries API and get the data. If you've used ui-router before you should be familiar with resolvers. For instance when we hit a route, say the countries route, we need to get some information from the API asynchronously and then redirect and render. ui-router's resolvers are simple as can be. Update your countries route with the following changes.

// countries state
.state('countries', {
    url: '/countries',
    controller: 'countriesCtrl',
    templateUrl: 'templates/countries.html',
    resolve: {
        countries: function($http) {
            var url = 'https://restcountries.eu/rest/v1/all';
            NProgress.start();

            return $http.get(url).then(function(response) {
                NProgress.done();
                return response.data;
            });
        }
    }
})

Now when you hit this route we get the list of countries and return the data, a micro progressbar indicates the status too. The Capital route needs a resolver as well, we want to get a country by capital name. Update your capital route with the following resolver:

// countries by capital state
.state('capital', {
    url: '/capital/:name',
    controller: 'capitalCtrl',
    templateUrl: 'templates/capital.html',
    resolve: {
        country: function($http, $stateParams) {
            var capital = $stateParams.name;
            var url = 'https://restcountries.eu/rest/v1/capital/' + capital;
            NProgress.start();

            return $http.get(url).then(function(response) {
                NProgress.done();
                if (response.data.length) {
                    return response.data[0];
                } else {
                    return {};
                }
            }, function(error) {
                console.log(error);
            });
        }
    }
})

If a capital city is not found, we return an empty object. So far so good.

 

Defining Our Controllers

Our templates have bindings to scope variables to display information. This data comes from the controllers, where we also handle user interactions. Update your app.js file and define the following controllers.

app.controller('homeCtrl', function($scope, $http) {
    $scope.capitalCity = 'Tehran';
    $scope.countryInfo = undefined;

    $scope.getCapitalCity = function() {
        var city = $scope.capitalCity;
        if (city.length == 0) return false;

        var url = "https://restcountries.eu/rest/v1/capital/" + city;
        NProgress.start();
        $http.get(url).then(function(response) {
            NProgress.done();
            if (response.data && response.data.length) {
                var info = response.data[0];
                console.warn(info);

                $scope.countryInfo = info;
            }
        }, function(error) {
            NProgress.done();
            if (error.status == 404) {
                $scope.countryInfo = undefined;
                alert('Capital Not Found!');
            }
        });
    }
});

app.controller('countriesCtrl', function($scope, countries) {
    $scope.countries = countries;
    $scope.query = '';
});

app.controller('capitalCtrl', function($scope, country) {
    $scope.country = {};
    if (country !== undefined) {
        $scope.country = country;
    }
});

Let's examine what we've done.

Home Controller

Here we have two scope variables: capitalCity and countryInfo. The first one is used as the model of our input element, the capital name. And the second variable is used to display the country information. The go button is bound to a function named getCapitalCity which gets the data. The countries argument comes from the resolver function.

Capital Controller

This controller has a single property to hold the country information. The country argument comes from the resolver.

 

Other End Points

REST Countries has other end points for retrieving data. You can get countries by region, calling code etc. Integrating other end-points shouldn't be difficult, it follows the same process. For now we can get the list of countries, search by capital and filter results. I will update this sample project in the near future and complete the app :wine:

 

CSS Rules

Update your style.css file with the following. Just a few simple rules for better visuals.

body {
    font-family: 'Roboto', Tahoma, sans-serif;
}

.content-wrap {
    padding: 15px 20px;
    margin-bottom: 20px;
    border: 1px solid #E5E5E5;
    border-radius: 4px;
}

.country-card {
    border: 1px solid #ddd;
    padding: 0 15px 20px 15px;
    margin-bottom: 10px;
}

 

Summary

And there it is, a simple angular app to use REST Countries :candy: This project uses a basic setup for angular apps. Of course in production, you'll want to make a few changes. You want to use a task runner like Gulp and automate your builds. Compile, concat and minify your resources and optimize the application. You get the idea.

We have seen how to create a minimal application, configure routes and resolvers with ui-router, showing progress indicators and communicating with a RESTful API.

The source is also available as a fiddle, as you can see below. You'll notice that I've defined our templates as inline here. Anyway have fun playing with Angular and APIs! I update my posts and sample projects frequently, so you make sure to visit again.

 

Live Example

Last Update: 23 Nov 2016


armin-zia

Written by Armin Zia

I'm a senior software developer focused on the Microsoft.NET Platform and Web Technologies. I provide consultation and teach short courses and workshops. When not coding, you can find me hiking, playing the guitar and video games, surfing YouTube and enjoying good reads. You can always reach me on the Contact page. Have a project or job offer you'd like to discuss? Ping me on the Hire Me page. If you've found this post helpful, be social, sharing is caring.



Share this post


Leave a comment