This is the first part of the tutorial for building native mobile application based on AngularJS framework, HTML5 markup and CSS styles. The second tutorial part can be found by following the link.
I have recently started using AngularJS a JavaScript tool set for building HTML5 applications. As we are covering cross-platform mobile application development here on htmlcenter, I decided to write a tutorial and share my experiences while using powerful and quite easy to understand AngularJS MVC structure for developing cross-platform mobile applications.
This tutorial is about how to build Angular powered HTML5 mobile application, wrap it up with PhoneGap and distribute as a native mobile app (on several mobile platforms if you wish so). We have split the tutorial into 2 parts, just to make it easier for information to sink in. As always, links to the example source code can be found at the end of the tutorial posts.
What are we going to cover?
First, we will briefly explore what is AngularJS (however, this tutorial is not about learning the basics), we are going to pick Ratchet as a front-end framework for our HTML5 mobile application and create MVC structure. Second tutorial part covers how to make our view transitions animated, how AngularJS communicates with web services and how to wrap up AngularJS application with PhoneGap. Sounds like we have some ground to cover. Lets get started.
Why AngularJS?
AngularJS is a framework for developing HTML5 applications. Its has alternatives like Backbone and EmberJS (and the bunch of others). My reason for picking AngularJS among others was rapidly growing developer community around this framework and resource support provided to it by Google. Any framework needs at least these 2 elements (passionate community and leading tech company support) to be successful. And AngularJS supports 2 way data binding a well, which lets you to build real time user experiences in apps.
As I have mentioned before, we are not going to cover the very basics of AngularJS in this tutorial, they have relatively good getting started guides, tutorials and videos online. One particularly good video for starting beginning AngularJS is created by Dan Wahlin. We wrote detailed tutorial on HTMLCenter about creating your first – Angular Starter application, worth checking it our for understanding Angular project structure.
Having said all that, I have tried my best to comment and explain the sample code created during this tutorial.
Selecting Ratchet
Ratchet is a collection of iOS-ish looking HTML5 components. Guys did a good job creating and styling HTML elements which look quite similar to native iOS ones. And you can always tweak them to suit application design by only changing CSS properties. For our purpose we will not be using any JavaScript files supplied by Ratchet as we are going to create our own.
Go ahead and download the latest version of Ratchet. Once downloaded, create a separate new directory with the name www
and copy only the files we are going to need in the future steps. These are index.html
and style .css
files. My new directory looks as below. Other nested directories in this project are for organizing JavaScript files, images and HTML code for our application views. For now just create an empty directories with these names
Getting Angular and naming the project
Go ahead, download latest AngularJS file and place it in js
directory. In the example project I’m using minimized version angular.min.js
.
Now, before starting to write any code we have to decide on what our example application will be, how will we name it and how many views we want to have.
I’m not a big movie watcher but once I decide to watch one – Rotten Tomatoes is the place I go to find useful reviews about the film. And they have an API for sharing data. Our app can show 5 great movies to watch. Sounds like a good idea for example project.
We are going to call it Pomidoro (European version of tomato). As for application views we will have 3 of them. First view is going to be the main view displaying 5 suggested movies in the list. Second view will show details of the Theaters this specific movie is currently on shown and the third view will be settings area for application user to change. We will have tab bar controller for navigating between the views. And we want transition between the views to be animated, to mimic actual native mobile app behavior. All the testing and debugging will be performed in Chrome web browser.
HTML and JavaScript code for Pomidoro mobile app
Looking at index.html
file which we copied from Ratchet we see that it needs some changes. First we have to remove all the references to ratchet.js
as this is no use for our project. Next we have to make sure all the paths to style files and images are actually matching our directory structure. Here is what I have after these changes are made
<!DOCTYPE html> <html> <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> <header> <h1>Pomidoro App</h1> </header> <nav> <form> <input type="search" placeholder="Search"> </form> </nav> <nav> <ul> <li> <a href="#"> <img src="img/icon-home.png"> <div>Movies</div> </a> </li> <li> <a href="#"> <img src="img/icon-profile.png"> <div>Theaters</div> </a> </li> <li> <a href="#"> <img src="img/icon-settings.png"> <div>Settings</div> </a> </li> </ul> </nav> <div> <ul> <li>Recommended movies</li> <!-- repeat this for all 5 movies in the list --> <li> <a href="#"> <div><img src="img/argo.png"></div> <strong>Movie Name</strong><br /> <p>Some sort of movie description</p> </a> </li> </ul> </div> <script src="js/angular.min.js"> <script src="js/app.js"></script> </body> </html>
In this HTML file we are referencing styles and AngularJS toolkit. The main JavaScript code for our application will go to app.js
. I have only one movie listed in the unordered list because we will use AngularJS to do the magic for us while populating all of them. Our movie will have image url, movie name and movie description as parameters.
Now, taking a look at app.js
. First we are going do is to create AngularJS application module and call it by our application name
var pomidoroApp = angular.module('pomidoroApp',[]);
Once we have the module created we can start adding routes with controllers and views to it. If you have some MVC (module, view, controller) structure experience you are going to like AngularJS right away. It truly complies to this structure and it allows developers to keep their data, app logic and any partial code (like views) separate and well organized. As we have previously decided, there will be 3 routes, which means 3 separate controllers with 3 separate views. Lets add them to the app.js
file. I’m also adding 2 factories for holding hard coded movie data (for now). Completed code for this file is below. I have commented it section by section so its easier to understand
// Defining angular application model // for Pomidoro app // var pomidoroApp = angular.module('pomidoroApp',[]); ////////// ROUTING ///////////////////////// // Deffining $routeProvider for Pomidoro applicatiom module // pomidoroApp.config(function ($routeProvider) { $routeProvider // We are going to define routes, // controllers and templates associated // with these routes. // You can change these but make sure // you know what you are doing // // main route // .when('/', { controller: 'RootController', templateUrl: 'views/RootControllerView.html' }) // theaters list page // .when('/theaters', { controller: 'TheatersController', templateUrl: 'views/TheatersControllerView.html' }) // settings page // .when('/settings', { controller: 'SettingsController', templateUrl: 'views/SettingsControllerView.html' }) // if non of the above routes // are matched we are setting router // to redirect to the RootController .otherwise({ redirectTo: '/'}); }); ///////// CONTROLERS //////////////////////////// // Below we are going to define all the controllers // we have defined in the router // // 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(){ $scope.recommendedMovies = recommendedMoviesFactory.getRecommended(); } }); // TheatersController // pomidoroApp.controller('TheatersController', function($scope,theatersFactory){ // This controller is going to set theaters // variable for the $scope object in order for view to // display its contents on the screen as html $scope.theaters = []; // Just a housekeeping. // In the init method we are declaring all the // neccesarry settings and assignments init(); function init(){ $scope.theaters = theatersFactory.getTheaters(); } }); // SettingsController // pomidoroApp.controller('SettingsController', function($scope){ // This controller is going just to serve the view }); ///////////// FACTORIES //////////////////////////// // Defining recommendedMovies factory // It has 5 recommended movies and // makes them available to controller // so it can pass values to the template // pomidoroApp.factory('recommendedMoviesFactory', function(){ var recommended = [ { name: 'World War Z', description: 'The story revolves around United Nations employee Gerry Lane (Pitt), who traverses the world in a race against time to stop a pandemic', img: 'img/wardwarz.png'}, { name: 'Star Trek Into Darkness', description: 'When the crew of the Enterprise is called back home, they find an unstoppable force of terror from within their own organization has detonated the fleet and everything it stands for', img: 'img/intodarkness.png'}, { name: 'The Iceman', description: 'Appearing to be living the American dream as a devoted husband and father in reality Kuklinski was a ruthless killer-for-hire.', img: 'img/wardwarz.png'}, { name: 'Iron Man 3', description: 'When Stark finds his personal world destroyed at his enemys hands, he embarks on a harrowing quest to find those responsible.', img: 'img/wardwarz.png'}, { name: 'Django Unchained', description: 'Set in the South two years before the Civil War, Django Unchained stars Jamie Foxx as Django', img: 'img/wardwarz.png'} ]; var factory = {}; factory.getRecommended = function(){ // If performing http communication to receive // factory data, the best would be to put http // communication code here and return the results return recommended; } return factory; }); // Defining theatersFactory factory // In this example it has 5 movie theatres // but in real live application you would // want it to get this data from the web // service, based on the the movie selected // by user // pomidoroApp.factory('theatersFactory', function(){ var theaters = [ { name: 'Everyman Walton', address: '85-89 High Street London'}, { name: 'Ambassador Cinemas', address: 'Peacocks Centre Woking'}, { name: 'ODEON Kingston', address: 'larence Street Kingston Upon Thames'}, { name: 'Curzon Richmond', address: '3 Water Lane Richmond'}, { name: 'ODEON Studio Richmond', address: '6 Red Lion Street Richmond'} ]; var factory = {}; factory.getTheaters = function(){ // If performing http communication to receive // factory data, the best would be to put http // communication code here and return the results return theaters; } return factory; });
As a summary, we have just created 3 different routes (which means that once this specific url is requested by browser AngularJS will ask corresponding controller to serve specific view) and 3 controllers which are in charge of serving specific view elements stored in .html
files and binding any data with these views.
We can now go back to our index.html
file and cut out all the HTML markup leaving just a placeholder for HTML content which is going to be loaded from view files by AngularJS. For this to happen we will add directives to HTML element tags so AngularJS knows what do we want to achieve. Such approach makes our app a single page application (or SPA as developers like to call these type of apps). Here is the code of trimmed down index.html
which now looks nice and tidy
<!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> <!-- placeholder for view markup --> <div ng-view></div> <script src="js/angular.min.js"></script> <script src="js/app.js"></script> </body> </html>
You will notice directives I have added to <html>
and the <div>
elements which now serves as placeholder for application content.
So what will happen now once /
route is requested by the browser? The index.html
file will be served and AngularJS will prefill <div ng-view>
with contents served by our RootController
. And from the app.js
code we see that this controller is going to serve RootControllerView.html
. What are the contents of this file?
<header> <h1>Pomidoro App</h1> </header> <nav> <form> <input type="search" placeholder="Search"> </form> </nav> <nav> <ul> <li> <a href="/"> <img src="img/icon-home.png"> <div>Movies</div> </a> </li> <li> <a href="#/theaters"> <img src="img/icon-profile.png"> <div>Theaters</div> </a> </li> <li> <a href="#/settings"> <img src="img/icon-settings.png"> <div>Settings</div> </a> </li> </ul> </nav> <div> <ul> <li>Recommended movies</li> <li data-ng-repeat="movie in recommendedMovies"> <a href="#/theaters"> <div><img src="img/hollywoodcat.png"></div> <strong>{{movie.name}}</strong><br /> <p>{{movie.description}}</p> <span></span> </a> </li> </ul> </div>
Here we have our menu items, and the rest of HTML markup from the front page. Most interesting part is data-ng-repeat
directive in <li>
element. Its tells AngularJS to repeatedly print this element until some condition is met. In our case its as long as we have any elements in recommendedMovies
data array (this is defined in controller). And for each element we are going to print out the name, description and picture (well, to make things a bit of fun I have added a lol cat as a visual representation of these movies. I’m sure you can do better in your apps..). I also have applied the same structure to theatersFactory
controller and the view for displaying available theaters. Here is how our application now looks after the changes (tested in Chrome web browser).
Whats next?
Congratulations on reaching so for with this tutorial. You have created core for mobile HTML5 application powered by AngularJS. There are few bits still remaining though. We need to add animation between loading the views (to create experience mobile app users are used to), it would be nice to have back buttons on the top navigation bar in order for app user to navigate back to previous view. And we have a bunch of hardcoded data about movies, this can be retrieved through HTTP communication to RottenTomatoes API. I have covered these topics in the second part of this tutorial. Follow htmlcenter for the stream of similar tutorials. Today’s application code lives here. Have fun.
18 Responses
Hi, this app is not working in my chrome browser. Any thoughts?
Hey, any specific errors?
hi, should this app work on your mobile smoothly? in my app the list can’t scroll, i am confused and don’t know why
Roc, what mobile device you are testing on? This tutorial uses Rachet front end framework which is built for iOS and doesnt really work well on Android devices
looks like a nice tutorial, definitely will try it.
But for now, I am very new to AngularJS, and have a question regarding the routes.
So if I have to show the data from the web service call in a grid (for this I’m using ng-grid), how do I do it?
I could inject it in my module and make it work individually when I’m not using route, but when I try to show it in a grid while using route, it just does not loads anything in my view template. Please help me with this.
Here is what my current code looks like:
I can see that you are missing some braces. your last bit of code should look like this:
Also how do your partials view1.html and view2.html look?
hey, can you tell me one thing.
Is it possible to replace angular routers urls like
index.html
toindex.html/addprojects
in the simulator?Hi Siva,
What are you trying to achieve by changing the routes?
Routes are actually defining what will happen if browser hits the specific route. For example:
will render specific view with html and dynamic tags.
Hi Saul,
how do you show errors? what are css or tags to display?
sridhar
Hey Sridhar,
Good question. As the Ratchet HTML framework I’m using in these tutorials doesn’t have specific elements set for representing errors or success fields, the closest UI element for representing error information can be a Ratchet’s red button.
Or if you are familiar with HTML/CSS I guess you can edit the existing styles. If you decide to do so, let me know and I can add the link to your example on htmlcenter blog.
Good day Sir.
I followed you tutorial and It really jump started me into building my own applications. Thanks.
I have a situation though: I’m building an application using both angularjs and ratchet. Problem is I want to use the tabbed widget which requires using links with #id for navigating through the tabs. But #ids are also used by angular for navigation. Any ideas on how i can sort this out???
Thanks in advance,
Daniel
Does you application runs as a web page (via an URL on Chrome), or as a web app (by launching from screen, like a chrome app) ?
Thank you
Hello Phu,
This application runs in the web browser. Its only html and JavaScript so you can just download the files and run them from you mashine
look like a awesome article..
The application works on Safari and Firefox, but fails on Chrome with the following errors:
XMLHttpRequest cannot load file:///Users/georgetarasenko/Angular/angularjs-mobile/Pomidoro/views/RootControllerView.html. Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource.angular.min.js:106 $get.idangular.min.js:102 oangular.min.js:100 $get.gangular.min.js:79 j.promise.then.iangular.min.js:79 j.promise.then.iangular.min.js:80 (anonymous function)angular.min.js:92 $get.e.$evalangular.min.js:90 $get.e.$digestangular.min.js:92 $get.e.$applyangular.min.js:16 (anonymous function)angular.min.js:27 dangular.min.js:16 cangular.min.js:16 xbangular.min.js:15 rcangular.min.js:178 (anonymous function)angular.min.js:125 aangular.min.js:24 (anonymous function)angular.min.js:6 nangular.min.js:24 c
angular.min.js:63 Error: Failed to execute ‘send’ on ‘XMLHttpRequest’: Failed to load ‘file:///Users/georgetarasenko/Angular/angularjs-mobile/Pomidoro/views/RootControllerView.html’.
you need to run it from a web server if you’re going to use chrome
Too many tips and suggestion already have given here on how to build angularjs mobile app development. Read 5 “NO” Signals in AngularJs Web Development. These signal definitely will help you to understand and developing quality angularjs based native mobile application. Learn https://www.linkedin.com/pulse/5-signals-angularjs-web-development-saroj-naagar
Amazing article! I would like to share some information like the latest version of AngularJS makes it possible to work outside the web browser and developed NativeScript is now working closely with Google developers to make Angular work with NativeScript smoothly.