JavaScript
Article
By Tanay Pant

Making API Calls in AngularJS using Angular’s $http service

By Tanay Pant

Note: This article was updated on 2016.01.17 by Siyuan Hua.

  • Replaced unnecessary setTimeout and clearTimeout calls with ng-model-option debounce feature and $watch service.
  • .success()/.error() have been deprecated, updated to use .then(success_callback, error_callback) instead.
  • Fix the broken poster image.

Nowadays, it is commonplace for web apps to communicate with each other via APIs. For example, when you buy movie tickets online, the movie ticket web site uses a remote API to verify your credit card information is correct. In this tutorial I will examine how AngularJS can be used to make HTTP requests to a remote API and how to handle the API’s JSON response so that the view is updated.

Staying with the movie theme, I will demonstrate this by building a movie browser called Fastr, which will fetch a variety of different information about any movie you care to enter. In addition to AngularJS, Fastr will be built using Bootstrap for styling and Animate.css for some snazzy effects.

This is what we’ll end up with:

A screen shot of the FASTR app

The code for this project is available from our GitHub repo, or you can view a working demo on CodePen.

The Project Structure

We will be keeping the code in a modular structure as follows:

css/
  animate.min.css
  bootstrap.min.css
  style.css

js/
  angular.min.js
  app.js

partials/
  main-info.html
  related-results.html

index.html

The file index.html will contain the main view for our app. The majority of it is boilerplate, but let’s examine where the action takes place:

<div class="input-group search-bar">
  <input type="text" 
         ng-model="search" 
         ng-model-options="{ debounce: 800 }"
         placeholder="Enter full movie name" />
  ...
</div>

<div id="main-info" 
     ng-include="'partials/main-info.html'">
</div>

<div id="related-results" 
     ng-include="'partials/related-results.html'">
</div>

As you can see, we have used ng-model to bind the input field (where the users will enter the movie name) to the search model (which we will declare in our controller). We have also used the ng-model-options directive with a debounce value of 800 to ensure that the model is updated with a delay of at least 800ms. We are also monitoring search model for changes using the $watch service and we register a callback to fetch data every time a change is detected. You will see this in the below section.

The main-info and related-results divs will be used to display information about the current movie and a list of related movies respectively. The information will be displayed in partials which are fetched, compiled and included by the ng-include directive.

Calling the API for Data

Let’s look at app.js which is the heart of the application. We start by passing $scope and $http as parameters to our controller’s constructor function. This means we are declaring dependencies on both the scope object and the http service.

.controller('MovieController', function($scope, $http){

Now, when the page loads for the first time, the search model is undefined. So, we set it to “Sherlock Holmes” and call the fetch function, which will contact the remote API and ensure that the view is initialized.

Now in the MovieController, we set up monitoring of the search model and load the results when the string in the search box changes. What we want, is that the results should only be fetched after the user has stopped typing for 800 milliseconds (remember we used the ngModelOptions directive with a debounce value of 800). This prevents the application from making unnecessary calls to the API. We also wish to see the results instantly as we are typing (we don’t want to press enter or click on any search button).

$scope.$watch('search', function() {
  fetch();
});

Now we define the change function. It loads results when the string in the search box changes. What we want, is that the results should be fetched only after the user has stopped typing for 800 milliseconds. This prevents the application from sending unnecessary calls to the API. We also wish to see the results instantly as we are typing (we don’t want to press enter or click on any search button).

Then we initialize the search model to “Sherlock Holmes” in the controller, which in turn invokes the fetch() callback registered with the $watch service, which contacts the remote API and ensures that the view is initialized.

$scope.search = "Sherlock Holmes";

Next comes the fetch function. This function makes calls to the API and processes the JSON data that is sent in response. The API we will be using for our movie browser is the OMDb API—a free web service to obtain movie information. If you are curious as to how the API works, I encourage you to check out the comprehensive documentation at the link above.

To make the requests we use Angular’s $http.get function, passing it the API URL and a concatenated query string as parameter. Two requests to different URLs are made—the first to retrieve the main information about the movie, the second to retrieve related results.

On success we store the responses in a model called details and a model called related respectively.

function fetch(){
  $http.get("http://www.omdbapi.com/?t=" + $scope.search + "&tomatoes=true&plot=full")
  .then(function(response){ $scope.details = response.data; });

  $http.get("http://www.omdbapi.com/?s=" + $scope.search)
  .then(function(response){ $scope.related = response.data; });
}

Next comes the update function. This will be called when a user clicks on one of the related titles in the view. It accepts an object (containing information about the related movie) and sets our Search model to the value of that movie’s title. The $watch service still performs the magic to pull in the information about that movie when search model changes.

$scope.update = function(movie){
  $scope.search = movie.Title;
};

Finally we have a convenience function select which ensures that the entire text is selected when the user clicks in the text input.

$scope.select = function(){
  this.setSelectionRange(0, this.value.length);
}
--ADVERTISEMENT--

Handling the Response

Let us now analyse the partials/main-info.html file.

We start off using the ng-if directive to show the message “Loading Results…” if the list has not yet been loaded, followed by checking details.Response==='True' to see if the API has found a match when the request returns.

In the case that results were returned, we use the ng-src directive to examine the contents of details.Poster and either load the image it contains a reference to, or load a placeholder image if no image was available.

<img ng-src="{{ details.Poster=='N/A' ?  
                'http://placehold.it/150x220&text=N/A' : 
                details.Poster }}">

After that we use Angular’s data bindings to display the remaining movie details, before including four further links at the bottom of the page to external sites where the user can obtain more information about the movie. We render these links using the ng-href directive, as we are using Angular expressions in the href attribute and if the user clicks a link before Angular has a chance to replace the expression with its value, then things will break.

Finally, let us examine partials/related-results.html

<div ng-if="related.Response!=='False'">
  Related Results:<hr>

  <ul class="rel-results">
    <li ng-repeat="movie in related.Search">
      <a href="#" id="{{ $index + 1 }}" 
         ng-click="update(movie)">{{ movie.Title }}
      </a>, {{ movie.Year }}
    </li>
  </ul>
</div>

Again, we use ng-if to check the response before rendering anything. We then use the ng-repeat directive to iterate through the related model’s Search property, which contains (amongst other things) a list of movie titles related to the movie we were searching for.

We also use the ng-click to call our controller’s update function whenever a title is clicked. As explained above, the update function will ensure that information for this new movie is fetched and displayed.

Some Final Touches

We can add some noscript tags to index.html, so that it displays an error message if JavaScript is either disabled or not supported in the user’s browser. We can also include animate.css to add in some cool animations such as flipping the poster image, zooming in the content and bouncing of the related results. Using animate.css is as simple as specifying the name of the animation in the class of the element preceded by the string animation. E.g.:

<div class="animated zoomInRight">

And here’s the result!

Please note that I’ve hidden the related results in the embedded demo due to space constraints. To see them, simply view the demo on CodePen (which is a good idea anyway, as the demo is faster there).

See the Pen YXXQxj by SitePoint (@SitePoint) on CodePen.

Conclusion

In this tutorial I have demonstrated how to use AngularJS to make a request to a remote API and how to use Angular’s data binding mechanisms to immediately update the view with the results. Building a project like this can be a great way of learning a particular language or framework feature, so I encourage you to clone the repo and further improve the app.

Login or Create Account to Comment
Login Create Account
Recommended
Sponsors
Get the most important and interesting stories in tech. Straight to your inbox, daily.