AngularJS: Rendering Feeds

25 Apr 2013 | By Alex Young | Tags angularjs mvc angularfeeds

Previously

In last week’s part I introduced Yeoman and we created a template project that included AngularJS. You can get the source at alexyoung / djsreader. The commit was 2e15d97.

Workflow

The workflow with Yeoman is based around Grunt. Prior to Yeoman, many developers had adopted a similar approach – a lightweight web server was started up using Node and Connect, and a filesystem watcher was used to rebuild the client-side assets whenever a file was edited.

Yeoman bundles all of this up for you so you don’t need to reinvent it. When working on a Yeoman project, type grunt server to start a web server in development mode.

This should open a browser window at http://localhost:9000/#/ with a welcome message. Now the web server is running, you can edit files under app/, and Grunt will rebuild your project as required.

Key Components: Controllers and Views

The goal of this tutorial is to make something that can download a feed and render it – all using client-side code. AngularJS can do all of this, with the help of YQL for mapping an RSS/Atom feed to JSON.

This example is an excellent “soft” introduction to AngularJS because it involves several of the key components:

  • Controllers, for combining the data and views
  • Views, for rendering the articles returned by the service
  • Services, for fetching the JSON data

The Yeoman template project already contains a view and controller. The controller can be found in app/scripts/controllers/main.js, and the view is in app/views/main.html.

If you take a look at these files, it’s pretty obvious what’s going on: the controller sets some values that are then used by the template. The template is able to iterate over the values that are set by using the ng-repeat directive.

Directives and Data Binding

Directives can be used to transform the DOM, so the main.html file is a dynamic template that is interpolated at runtime.

The way in which data is bound to a template is through scopes. The $scope object, which is passed to the controller, will cause the template to be updated when it is changed. This is actually asynchronous:

Scope is the glue between application controller and the view. During the template linking phase the directives set up $watch expressions on the scope. The $watch allows the directives to be notified of property changes, which allows the directive to render the updated value to the DOM.

Notice how the view is updated when properties change. That means the property assignment to $scope in the template app will be reflected by the template.

If you’re of an inquisitive nature, you’re probably wondering how the controller gets instantiated and associated with the view. There’s a missing piece of the story here that I haven’t mentioned yet: routing.

Router Providers

The MainCtrl (main controller) is bound to views/main.html in app/scripts/app.js:

angular.module('djsreaderApp', [])
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
  });

The $routeProvider uses a promise-based API for mapping URLs to controllers and templates. This file is a centralised configuration file that sets up the application.

The line that reads angular.module sets up a new “module” called djsreaderApp. This isn’t technically the same as a Node module or RequireJS module, but it’s very similar – modules are registered in a global namespace so they can be referenced throughout an application. That includes third-party modules as well.

Fetching Feeds

To load feeds, we can use the $http service. But even better… it supports JSONP, which is how the Yahoo! API provides cross-domain access to the data we want to fetch. Open app/scripts/controllers/main.js and change it to load the (extremely long) YQL URL:

angular.module('djsreaderApp')
  .controller('MainCtrl', function($scope, $http) {
    var url = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20xml%20where%20url%3D'http%3A%2F%2Fdailyjs.com%2Fatom.xml'%20and%20itemPath%3D'feed.entry'&format=json&diagnostics=true&callback=JSON_CALLBACK";

    $http.jsonp(url).
      success(function(data, status, headers, config) {
        $scope.feed = {
          title: 'DailyJS',
          items: data.query.results.entry
        };
      }).
      error(function(data, status, headers, config) {
        console.error('Error fetching feed:', data);
      });
  });

The second line has changed to include a reference to $http – this allows us to access Angular’s built-in HTTP module.

The $scope is now updated with the result of the JSONP request. When $scope.feed is set, AngularJS will automatically update the view with the new values.

Now the view needs to be updated to display the feed items.

Rendering Feed Items

To render the feed items, open app/views/main.html and use the ng-repeat directive to iterate over each item and display it:

<h1></h1>
<ul>
  <li ng-repeat="item in feed.items"></li>
</ul>

This will now render the title of each feed entry. If you’re running grunt server you should have found that whenever a file was saved it caused the browser window to refresh. That means your changes should be visible, and you should see the recent stories from DailyJS.

AngularJS feed rendering What you should see...

Conclusion

In this brief tutorial you’ve seen Angular controllers, views, directives, data binding, and even routing. If you’ve written much Backbone.js or Knockout before then you should be starting to see how AngularJS implements similar concepts. It takes a different approach – I found $scope a little bit confusing at first for example, but the initial learning curve is mainly down to learning terminology.

If you’ve had trouble getting any of this working, try checkout out my source on GitHub. The commit for this tutorial was 73af554.


blog comments powered by Disqus