This is the second part of tutorial for building native mobile application based on AngularJS, HTML5 markup and CSS styles. In the first part you will find how to setup the new project, add routes, controllers, HTML5 views and do simple testing in a standard web browser.
What we are doing in this tutorial?
In this tutorial part we are going to take previously created application and add back navigation button, sliding animations between the views and functionality to retrieve data from web services by using asynchronous HTTP communication. Finally I’ll give some hints how to wrap this application into PhoneGap framework in order to create installable version. Lets get started.
Providing navigation buttons
Last time we noticed that it would be great to have navigation options for app users so they can return to the previously viewed application page.
As our example application is quite simple, adding HTML markup for navigation is a straight forward process. Ratchet (the front end framework we are using for this project) has back button styles already built in. We just have to add a link element above the title of navigation bar to TheatersControllerView.html
. Adding it below would create right side navigation bar button.
<header> <a href="#/">Back</a> <h1>Find a theater</h1> </header>
The back route we add is to the root view as there is only way app user can navigate to TheatersControllerView
. Other views in our app don’t need to have navigation back option.
Adding animations to AngulaJS projects
From the version 1.1.5 AngularJS framework has inbuilt support for animated transitions for certain directives. This currently can be done in 3 ways, CSS Transitions, CSS Animations and good old JavaScript way. There is even website dedicated solely to AngularJS animations and it does a good job explaining how stuff works (this is why a great developer community supporting the framework is so useful).
We are going to use CSS Transition in this tutorial by adding ng-animate
element to our HTML to let AngulaJS know we want to have CSS style animated transition. We have to provide this element with the value of our CSS animation name. While building HTML AngularJS will attach 2 CSS classes to the DOM element named according to the animation name we gave earlier. Given that our animation name is slide
the classes added by AngularJS will be slide-enter
and slide-enter-active
. Its possible to provide custom names for CSS classes as well. You would do it like this
ng-animate="{enter: 'slide-begin', leave: 'slide-exit'}"
Lets add simple CSS class code and ng-animate
element to our projects index.html
root file after which the file content should look like this
<!DOCTYPE html> <html data-ng-app="pomidoroApp"> <head> <meta charset="utf-8"> <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no"> <link rel="stylesheet" href="css/ratchet.css"> <link rel="stylesheet" href="css/app.css"> </head> <body> <style type="text/css"> /* Angular.js animated navigation */ .slide-enter, .slide-leave { -webkit-transition: 300ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; -moz-transition: 300ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; -ms-transition: 300ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; -o-transition: 300ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; transition: 300ms cubic-bezier(0.075, 0.820, 0.165, 1.000) all; } .slide-enter { left: 100%; } .slide-enter.slide-enter-active { left: 0px; } .slide-leave {} .slide-leave.slide-leave-active{ left: -200px; } </style> <!-- placeholder for view markup --> <div ng-view ng-animate="'slide'"></div> <script src="js/angular.min.js"></script> <script src="js/app.js"></script> </body> </html>
Now after testing the project code in Safari browser we can see a nice navigation effect after the particular movie from the list is clicked by user. This animation transitions us to the theaters controller and view. After testing on mobile devices I noticed that sliding effect is a little bit slow and has small lag as well. How to smooth it up is not the goal of this tutorial and most likely I’ll cover this subject in the separate post here on HTML Center. Next chapter is about adding web services communication functionality to consume Rotten Tomatoes API data and display it in our app.
Adding HTTP communication to AngularJS app
For performing HTTP requests AngularJS has a property named $http
(nice and simple naming). It also has strong support for promises. Promises (if you are not so much familiar with them) is a concept we use while dealing with async communication requests. Then we perform request and assign result of that request to some variable but do not expect it to have complete response right away. Because communication with 3rd party web service takes time. Therefore promise variables are guaranteed to have response data some time in the future.
In the previous tutorial part I have mentioned that we will be using Rotten Tomatoes API for retrieving movie data. Their API for developers is both simple and provides useful movie data. Simply go and register your own developer account with them to receive API key. It’s quite a straight forward process. Once you have API credentials the simple query to Rotten Tomatoes web service in order to get 5 hot movies currently shown in UK theaters is performed like HTTP Get request with the following string
http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json?page_limit=5&page=1&country=uk&apikey=YOUR.API.KEY
It is a good information security practice not to store your sensitive developer credentials data in app itself. As all applications are running on devices which you have completely no control off. Always try to perform communication with 3rd party API’s via back end code and keep mobile application as a simple client.
This approach will also help while dealing with Cross Origin Resource Sharing issues as JavaScript and Ajax HTTP communication needs extra attention to stay secure. On your backed you can configure CORS settings for specific requests origins etc. I have prepared very simple php based backed file which will only make a request to Rotten Tomatoes API and return all result data back to our mobile app
<?php // Simple tweak to allow access from any origin // Credits to http://stackoverflow.com/questions/8719276/cors-with-php-headers // if (isset($_SERVER['HTTP_ORIGIN'])) { header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}"); header('Access-Control-Allow-Credentials: true'); header('Access-Control-Max-Age: 86400'); // cache for 1 day } // Access-Control headers are received during OPTIONS requests if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS"); if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"); } /////////////////////////////////////// // SETUP ////////////////////////////// /////////////////////////////////////// // API credentials // $apikey = "YOUR ROTTEN TOMATOES API KEY"; // Number of items in response $returnitems = 5; $url = "http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json?page_limit=".$returnitems."&page=1&country=uk&apikey=".$apikey; // Building string for GET request // $requeststr = $url; // CURL for communicating with web service // $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$requeststr); curl_setopt($ch, CURLOPT_VERBOSE, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); $response = curl_exec($ch); echo $response; ?>
You will notice at the beginning of the file I have code to enable cross domain access for Ajax requests. You can limit this to specific origin, domain etc.
We will now update our factory and replace static and hard coded data we previously had with the $http.get
request. $http
is the service available to your applications if it uses AngularJS framework and is in detail described in official documentation. As we already discussed all HTTP communication is asynchronous and this means we have to handle the scenario where requested data is not right away available for our controller and the view. We add the following code to the factory method to recommendedMoviesFactory
// This is the place for performing http communication // with 3rd party web services. var url = 'your.backend.url' return $http.get(url).then( function(response){ return response.data; })
Now in the RootControler we are handling the fact that data coming from $http
can be delayed by adding ready flag to the $scope
element. This will let us display HTML elements in the view only once all the data is received from Rotten Tomatoes API
// RootController // pomidoroApp.controller('RootController', function($scope,recommendedMoviesFactory){ // Controller is going to set recommendedMovies // variable for the $scope object in order for view to // display its contents on the screen as html $scope.recommendedMovies = []; // Just a housekeeping. // In the init method we are declaring all the // neccesarry settings and assignments init(); function init(){ // As we need to wait for $http.get // request data to be ready we are // using .then on the promisse received // from factory recommendedMoviesFactory.getRecommended().then(function(data) { //this will execute when the //AJAX call completes. $scope.recommendedMovies = data.movies; $scope.ready = true; console.log(data.movies); }); }; });
What’s left is to update the RootControlerView
with the the information about new elements in the data provided by controller (notice in previous code example, I’m using console.log() to print out and investigate all the available JSON data structure supplied by Rotten Tomatoes API) and add a filter in order to filter list of 5 movies with the search input field. Here is the updated version of this file
<div> <ul> <li>Recommended movies</li> <li ng-show="!ready"> Receiving data... </li> <li ng-show="ready" ng-repeat="movie in recommendedMovies | filter:name"> <a href="#/theaters"> <div> <img style="float:left; width:120px; height:177px; margin-right:10px;"class="movieimg" src="{{movie.posters.profile}}" /> <strong style="font-size:18px;">{{movie.title}}</strong> <p style="font-size:16px;">{{movie.critics_consensus}}</p> </div> <div style="clear:both;"></div> </a> </li> </ul> </div>
In the above code I’m making use of ready
variable which is being set in controller and will be set to yes once all requested movie data is available. Until then I’m going to display "Receiving data..."
text in the list item element. ng-show
is the directive making this all happen as I only have to supply ng-show="!ready"
and AngularJS knows to hide it once variable is being set to true.
Now once project files are updated, reloading the page will give us the following result. By no means its a finished mobile application but it has a good foundations and MVC framework to build on to. But it has scroll view, dynamically loading content from external web services, animated transitions and is powered by AngularJS.
Testing AngularJS project with PhoneGap
As we were working in the directory called www
migrating this project to wraper like PhoneGap and make it installable app is quite easy. You just have to replace contents of the www
directory in your PhoneGap XCode project with the ones you created during this tutorial. If you want to understand how to create PhoneGap powered XCode application we have good tutorial for your here at HTML Center.
I hope you have enjoyed this tutorial, final source code for example application can be found hosted on the GitHub. As always I welcome all the comments, suggestions and advice.
5 Responses
Hey. This is quite useful and informative article.
Thanks
I just tried with keeping a basic php file in www folder and added below lines
<?php
echo "Hello Singapore!";
now in my index.php file I added the below code
{{response}}
var app = angular.module(‘myApp’, []);
app.controller(‘customersCtrl’, function($scope, $http) {
$http.get(“serverside/data.php”)
.success(function (response) {
$scope.response = response;
//alert(response)
});
});
after installing the app its just showing
<?php
echo "Hello Singapore!";
instead of printing only Hello Singapore.
Please suggest something.
Thanks in advance
hey, looks like your server is not running
php
or is not executing php index files. get in touch with your hosting provider.how to convert into .apk this code, if you know then suggest