Making API Calls in AngularJS using Angular’s $http service
Note: This article was updated on 2016.01.17 by Siyuan Hua.
- Replaced unnecessary
setTimeout
andclearTimeout
calls withng-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:
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);
}
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.