Resetting JavaScript variables when using Turbolinks

Every time you load a new page with Turbolinks only the body of the page is replaced. This means ...

Phalcon the PHP framework written in C

Over the past few years the PHP community has matured and evolved immensely The language itself ...

0
Tuesday, December 4, 2012
Building a simple single page application using AngularJS

I am going to walk you through building a SPA (single page application) using AngularJS, Google's latest JavaScript MVC framework. After developing applications using Backbone.js, Spine.js and good ol' jQuery I fell in love with AngularJS. It makes development of SPAs actually fun removing a lot of the boilerplate that most of the other frameworks make you write to get a simple app started.

The app we are going to build today is a simple multipage website that will read data in from a static JSON file. We will be covering setting up AngularJS, routing, rendering partials, communication between controllers and loading JSON asynchronously.

Sections

Before we get started it's a good idea to watch the intro videos on angularjs.org as they cover most of the basics. I'm going to assume that you understand the concepts of how the controller binds $scope with the views and how the ng-model directive works.

Setting up AngularJS

This tutorial will require you to run these files via a web server. If you have apache/MAMP/WAMP set up you should be able to create a new directory under your webroot and access via localhost. I like to run the npm module nws which creates a web server in the current directory allowing me to get started quickly.

Html

The first thing you will want to do is setup your module. You do this by adding the ng-app attribute to your html tag. In the case of our application we will call our module Site.

<html lang="en-US" ng-app="Site">

The next thing we want to do is set up a controller. The controller is bound to the element you add it to and it's children. We are going to add an AppController to our body tag which allows the entire body to inherit from this controller. To add a controller you use the ng-controller attribute.

<body ng-controller="AppController">

Here is a template including AngularJS from CDNJS that I will be using throughout this tutorial.

JavaScript

Now that our html is set up we need to create the module and controller in our /js/site.js file that is included.

var Site = angular.module('Site', []);

function AppController ($scope) {
}

The first line creates our Site module and the function defines our AppController. The controller receives one argument (at the moment) $scope which binds models between the view (html) and controller.

Routing

If you took a look at the template above you will have noticed that there are three links in the nav bar: Home, About and Contact. We want to be able to route to these links and load a section from the JSON file based on the slug.

The first thing we need to do is configure our Site module to use the $routeProvider and then set up our routes.

Site.config(function ($routeProvider) {
  $routeProvider
    .when('/page/:slug', {templateUrl: 'partials/page.html', controller: 'RouteController'})
    .otherwise({redirectTo: '/page/home'});
});

We are passing in an anonymous function to the modules config method. In the function we are passing in Angular's $routeProvider service which allows us to define routes. The two methods we are using are when() which takes a path and hash of options including the templatePath and controller and otherwise() which allows us to redirect to a route if one is not found.

We are defining one route for this application /page/:slug. This path contains the :slug parameter, we can use this later using the $routeParams service to extract the page data from the JSON.

We are also setting a controller for this route (RouteController). This controller is in charge of binding the page content to the view.

function RouteController ($scope, $routeParams) {
  // Getting the slug from $routeParams
  var slug = $routeParams.slug;

  // We need to get the page data from the JSON
  // $scope.page = ?;
}

Now when you try and visit the page you should be redirected to /#/home and if you try and type in anything else it will redirect you back.

Rendering Partials

When we defined our route we set a templateUrl. This points to a static html file that contains 2 simple expressions containing properties that will be bound to the RouteController's $scope.

<!-- partials/page.html -->
<h1>{{page.title}}</h1>
{{page.content}}

To include this partial into your layout you need to use the <ng-view></ng-view> directive. This will automatically be replaced with the partial set in the routes. You can see that this is included already in the template provided above.

Loading JSON

Let's load some data into this application. I've created a simple static JSON data store (in real life this would probably be generated by an API) which we will load in via Ajax when the application starts.

// pages.json
{
  "home": {
    "title": "Home",
    "content": "This is the home page. Welcome"
  },
  "about": {
    "title": "About",
    "content": "This is the about page. Welcome"
  },
  "contact": {
    "title": "Contact",
    "content": "This is the contact page. Welcome"
  }
}

We should only load the JSON file once, cache it to a variable and access the variable when we need the data. Let's use Angular's $http service to grab the JSON file. To use the $http service we will need to pass it as an argument to the AppController.

function AppController ($scope, $rootScope, $http) {
  // Load pages on startup
  $http.get('/pages.json').success(function (data) {
    $rootScope.pages = data;
  });
}

We are also passing in $rootScope which all scopes inherit from. We do this so this so that we can access the pages JSON data in our RouteController. We can then access the page data by using the slug we captured earlier and bind it to the scope so it's accessible in our partial.

function RouteController ($scope, $rootScope, $routeParams) {
  // Getting the slug from $routeParams
  var slug = $routeParams.slug;

  $scope.page = $rootScope.pages[slug];
}

Communicating between controllers

We've already demonstrated one way to communicate between controllers using the $rootScope. Another option is using events. In Angular you can emit events and listen to events in different controllers while passing data from the emitter to the listener.

We want to pass the slug from the RouteController to the AppController so that we can set the active class on the current menu link. Angular has a ng-class directive which allows you to add conditional classes to elements.

<li ng-class="{active: slug == 'home'}"><a href="/#/page/home">Home</a></li>
<li ng-class="{active: slug == 'about'}"><a href="/#/page/about">About</a></li>
<li ng-class="{active: slug == 'contact'}"><a href="/#/page/contact">Contact</a></li>

You can see that the RouteController is emitting the slug and the AppController is listening for the slug which then sets it onto it's scope and exposes it to the view.

function AppController ($scope, $rootScope, $http) {
  // Load pages on startup
  $http.get('/pages.json').success(function (data) {
    $rootScope.pages = data;
  });

  // Set the slug for menu active class
  $scope.$on('routeLoaded', function (event, args) {
    $scope.slug = args.slug;
  });
}

function RouteController ($scope, $rootScope, $routeParams) {
  // Getting the slug from $routeParams
  var slug = $routeParams.slug;

  $scope.$emit('routeLoaded', {slug: slug});
  $scope.page = $rootScope.pages[slug];
}

I hope you guys enjoyed this walkthrough. I will be writing more about AngularJS in the future. If you guys have any questions about this post or have any topics you are interested in let me know in the comments.

Tweet this post if you found it useful

Related Posts

Resetting JavaScript variables when using Turbolinks

Saturday, December 1, 2012  ·   0

Every time you load a new page with Turbolinks only the body of the page is replaced.... (continued)

Introducing Polytalk. A simple protocol which allows communication between different languages via TCP.

Monday, November 26, 2012  ·   0

Polytalk is my latest open source project. I developed it out of the need to communic... (continued)

Using Polytalk inside a Meteor Application

Sunday, December 9, 2012  ·   0

I've put together a simple demo that integrates Polytalk with Meteor. The app all... (continued)

Caching dynamic partials when using Turbolinks

Friday, November 30, 2012  ·   0

Yesterday I blogged about caching and loading dynamic partials via ajax for non stati... (continued)

0 Comments

Comment successfully added. View comment.×

Comments are now closed for this article.