AngularJS Tutorial:
Building a Metro-North Train Schedule App

November 14, 2015

I have been using AngularJS for a number of years now and absolutely love it. Being a full-stack developer, I can appreciate AngularJS modular architecture and how easy the framework allows data consumption from web services using AngularJS Services.

Eariler this year, I went to the One Hour App Challenge presented by Mark Lassoff at the Stamford Innovation Center. In the course of an hour, Mark was able to create a Metro-North PhoneGap app, which I thought was pretty cool. Anyways, the idea was born - I wanted to create a similar app using AngularJS.

The app we're about to create can be previewed here: AngularJS Metro-North App and full source code is in the following repo:

The app will have two screens (train stops & schedule):
AngularJS Metro-North App: Stations
AngularJS Metro-North App: Schedule
As you can see from above, the app is rather simple and maybe even somewhat useless; however, the app can be a great source of learning and incorporates the following AngularJS concepts: Single-Page Appication, Routing, Controllers, Services, Directives, and also Bootstrap.

Step 1: Configure WebServer

If you're seasoned developer with IIS/Apache/XAMPP/MAMP running, congrats - you can skip this section. Otherwise, you'll need to have a webserver up-and-running in order to build AngularJS applications.

If you're on Windows, try googling for XAMPP or configure the IIS or install Python and use the command below.

If you're on MAC/Linux, things are a bit simpler: create a new folder (e.g. MyAwesomeApp) then using the Terminal, go to that folder and type:
sudo python -m SimpleHTTPServer 89

This should start a webserver on port 89. The site should be accessible now using the following address: http://localhost:89.

Step 2: Create an HTML File

The HTML file will essentially host our AngularJS application and will provide a shell for AngularJS and other libraries.

Make sure you can navigate to your page . You should see the following:

Step 3: Adding Required Libraries: AngularJS and Bootstrap

We need to add AngularJS libraries to the project. Since we'll be using Bootstrap CSS we'll also need to add a Bootstrap CDN reference. I assume you know what AngularJS is and how the framework works. If not, read the following tutorial.

We'll be using AngularJS 1.4.5 and Bootstrap 3.3.5 and we’ll need to add AngularJS core, routing, and resource libraries to our code:
Add the following lines to your HTML file :

<!-- AngularJS 1.4.5 core library from Google CDN  -->
<script src=""></script>

<!-- AngularJS 1.4.5 routing library from Google CDN  -->
<script src=""></script>
<!-- AngularJS 1.4.5 resource RESTful service interaction from Google CDN  -->
<script src=""></script>

<!-- Bootstrap CDN link -->
<link rel="stylesheet" href="" integrity="sha512-dTfge/zgoMYpP7QbHy4gWMEGsbsdZeCXz7irItjcC3sPUFtf0kuFbDz/ixG7ArTxmDjLXDmezHubeNikyKGVyQ==" crossorigin="anonymous">

Your HTML should look as following now:

Step 4: Adding AngularJS Markup

We'll need to modify our HTML page to include some of the required markup. Add the following to the main HTML tag:
<html lang="en-US" ng-app="angularJSMetroNorth">
This will essentially initialize our AngularJS application (more on this below). Because we’re building a Single-Page Application, we’ll need to enable AngularJS routing via the "base" tag so add the following tag right after Bootstrap CDN reference:
<base href="/"/>
Finally, since we're using AngularJS templates we'll need to tell our application where the templates will be displayed. This is done using the "ng-view" directive:
<div ng-view>
My Awesome Angular Project
Your HTML should look as following now:
If you refresh the browser and look at Developer Console (using Chrome go to Settings -> More Tools -> Developer Tools then selecting the “Console” tab), you'll see the following error:

What you’re seeing is normal. AngularJS simply complains about our application (angularJSMetroNorth), which we haven't created yet.

Step 5: Creating AngularJS Application

Let's go ahead and create the AngularJS Metro-North app. Our app will comprise of the following components:
  • Main Application (app.js) - handles routing, dependency injection, and other configuration and utility methods.
  • Controllers (controllers.js) - application countrollers that work with services and provide data to templates
  • Services (services.js) - application service layer that handles data retrieval, caching, storage, etc
  • Directives (directives.js) - application utility methods that handle various UI functions.
  • Templates (partials/*) - application HTML templates
Create a new folder called "assets" and then another sub-folder called 'ngapp'. Then create the following JS files inside the "ngapp" folder: app.js, controllers.js, directives.js, and services.js

Now, go ahead and create a sub-folder (inside the "ngapp" folder) and call it "partials". Inside the "partials" folder create two files called "train-schedule.html" and "train-stations.html".

Your folder structure should look as following:

AngularJS Application File: App.js

Copy the following code into your "app.js" file.
This file setups our application (angularJSMetroNorth) and any code dependencies (e.g. ngRoute). Also, notice how AngularJS routing is setup - our app will have the following routes:
  • / - homepage, handled by the TrainStationCtrl controller
  • /schedule/:id - train schedule, handled by the TrainScheduleCtrl controller. The controller expects a Train Stop ID parameter (i.e., :id designates a numeric ID parameter)
The AngularJS application definition file also defines a few utility methods, such as the window.log, for logging and debugging purposes.

AngularJS Controllers File: Controllers.js

Copy the following code into your "controllers.js" file.
The application controller handles application logic for each route defined in the "app.js". The controllers work with services and other libraries to retrieve and process data. You can see how service promises get resolved here (e.g. MTADataService.getTrainStations().then(function ...).

Take a look at your "app.js" file and see how it injects controller definitions into the code. Notice how module names get referenced (e.g. angularJSMetroNorthControllers).
angularJSMetroNorth.config(['$routeProvider', '$locationProvider', '$resourceProvider',
  function ($routeProvider, $locationProvider,$resourceProvider) {
This is called dependency injection - a very powerful concept in AngularJS.

AngularJS Services File: Services.js

Copy the following code into your "services.js" file.
The application services handle data retrieval and data sharing (e.g. DataShareService) between different controllers. The service module can call an internal web service or an external one using AngularJS HTTP library ($http).

The two service calls (MTATrainScheduleService and MTATrainStationService) are almost identical; they simply call the data service (in our case a local JSON file) and retrieve data. The app has been tested with the live MTA data feed (the link included). In order to use the live feed, you'll need to register with MTA and pray the service works (didn't work for me initially).

Look back at the "controllers.js" and see how the service calls are being used and referenced. Again, DI (depedency injection) is at play here.

AngularJS Directive File: Directives.js

Copy the following code into your "directives.js" file.
This is a very simple directive that will help us with date formatting (what a joy doing this in JavaScript). For each row, we would simply include the following tag <span metro-north-date-formatter="JS-DATE-OBJECT"></span> and AngularJS will convert that into a formatted date string.

AngularJS Templates: Train-Schedule.HTML and Train-Stations.HTML

Copy the following code into your "train-schedule.html" file partial. Again, make sure the file is stored in the "assets/ngapp/partials" directory since this is where controller routing in the "app.js" is defined. Copy the following code into your "train-stations.html" file.
The templates file simply iterate through data records (passed from the controller using $scope).

AngularJS Main HTML: Index.html

We now need to update the main HTML file (/index.html) and include the newly created AngularJS application files.
Copy the following code into the "index.html" file.
We haven't made too many changes - simply added the references to our JS files (lines 25-28)) and also added some markup using the Bootstrap "jumbotron" class (lines 37-41).

If you refresh your browser, you should see the following:

And if you look at the Developer Console, you should see the following:

So, what's happeing here? Well, the app is working fine; however, because we haven't created the data files yet, no information is being returned and displayed in the app.

If you can't see above results on your computer, double check your files and make sure everything is properly referenced, injected, and configured.

Step 6: Adding Data Files

The last step of our application is to add JSON data files to the application. Because we are not using the live data feed (requires MTA registration and the Public Key and email to their SysAdmin to make sure the API works) we will need to to hardcode the data to make the app work.

Create a new folder inside the "assets" folder and call "data". Then download the following files: train-stations.json and train-schedule.json and save them in the "data" folder.

Your application folder should now look as following:


You should have a fully working app now. If not, look at the Developer Console and see what's not working. Feel free to email me with questions/suggestions. The full source code is available in the repo:

Main Application Screen (notice all the debugging information in the Console) accessible via http://localhost:89/

Train Schedule Screen (after selecting one of the stations) accessible via http://localhost:89/schedule/1

Email me () if you have any questions or suggestions. Written by GlebP, 2015.