AngularJS and Drupal -- Retrospective on the project

So after posting my first attempt at melding AngularJS and Drupal, I decided to do a retrospective on some of the Angular aspects of my code, since my first post focused mainly on the refactoring aspects, most of which happened on the Drupal side.

Let's start with the template file.

A brief tour of the template file

If you pull up the template file, you will see something similar to the following:

Now, the template file uses a number of "directives", which is a core feature of AngularJS. A directive is effectively a marker on a DOM element that Angular uses to attach a behavior to that element. Angular directives are prefixed with "ng-", so they are pretty easy to identify.

Our template file uses the following directives:

  • ng-app
  • ng-controller
  • ng-model
  • ng-cloak
  • ng-repeat
  • ng-click
  • ng-template
  • ng-src

ng-app

The ng-app directive is the mother of all directives. This directive tells AngularJS which part of the DOM it is managing. Everything inside the DOM element marked with this directive is under the purview of AngularJS. You will have one, and only one, "ng-app" directive on any given page.

ng-controller

In Angular, areas of a page are managed by JavaScript classes called controllers. The ng-controller directive allows you to specify which particular controller is managing a particular area. In our case, we have specified that the "ListController" is managing everything inside the first nested "div" in our template file. The code for that controller resides in our ang.js file.

ng-model

The ng-model directive creates a data binding between the DOM element it's attached to (in our case the "input" text field) and the specified element (in our case the "nodeFilter", which i'll cover under the "ng-repeat" directive.

ng-cloak

ng-cloak prevents the Angular template (in our case the the contents of angular-listing.tpl.php) from displaying in its raw state. This is to prevent the "flickering" that occurs while the DOM element expressions wrapped in binding markup managed by AngularJS are being populated. It works in conjunction with CSS rules embedded in the main angular framework script.

ng-repeat

The ng-repeat directive instructs Angular to repeat this DOM element, in our case the "li" tag and its contents, for each item in the specified javascript array object (in our case "nodes"), substituting the specified property of each instance of "node" for our expressions, which are wrapped in binding markup designated by double curly brackets {{ }}. We also have a few other features on this element specific to the ng-repeat directive, which are "filter:", "orderBy:", and "limitTo:".

filter: works with the directive we specified earlier on our "input" tag, ng-model, to bind our input element to filter our results, listed here in our unordered list, by the specified input from the user. In our case, we are using this to filter down our results list based on the node title.

orderBy: specifies an item to sort any results by (in our case, the node title).

limitTo: specifies the number of results items we want displayed on the page. A note here: even though we are displaying 150 items, we are able to filter the entire result set (in my case, 5000 nodes), returning the first 150 items that match our filter contents, and updating that result dynamically as you type in more characters in the filter input.

ng-click

ng-click attaches a behavior to an element that is triggered by a "click" event.

ng-template

ng-template specifies that the contents of the script tag where this is attached are to be loaded into the $templateCache for use by other directives (in our case, the ngDialog directive in our application script).

ng-src

The ng-src directive is used in place of the "src" attribute in an image tag to allow the use of AngularJS expressions (specified by {{ }}) within our "src" attribute).

That covers all of the Angular directives. You can look at the documentation (or use your google-fu) to find out more about each one specifically. Now for our Angular app code, which resides in ang.js.

ang.js

Our Angular application code is contained in ang.js, and is as follows:

Our angular module, called "nodeListing" (note that this corresponds to our "ng-app" directive in our template file), contains two components: a "factory" class and a "controller" class.

Controllers are an integral part of an AngularJS project, since the precept of Angular is MVC/MVVM based. Controllers manage the interactions between the "Model" and the "View", and act as the manager for different components of the Angular application. Controllers interact with the View through the use of the $scope object; both Views and Controllers have access to the $scope object. $scope is used to store data that is needed by the View to render output.

Fellow Drupalers, please note: the View we speak of here is not a Drupal View, as provided by the Views module, but an Angular View.

Factories (and Services and Providers) serve a special purpose within an Angular application. They follow the Singleton design pattern, in that only one instance of a specific Factory or Service or Provider exists per Angular application instance. Their role is to store the more complex logic of the application, such as business rules and calculations, and to serve as or interact with data stores (Models) (so your ajax calls would typically reside in one of these). Any task that might be re-usable throughout your application should be placed in a Factory or Service.

Tyler McGinnis has a blog post that has a very good explanation and illustration of the differences between Factories, Services and Providers. Please read up on it there, as he does a great job explaining it much better than I.

AngularJS provides a number of services out of the box; we use the $resource factory in our application. $resource provides an object that lets us easily interact with RESTful server side data sources, and is the tool we use to interact with our JSON output provided via the (Drupal) Views Datasource module and our custom (Drupal) views. The $resource object is injected into our Node factory, and handles our requests to our service endpoints.

Our Controller, ListController, handles the interactions with our Angular View (template) and the interactions with our Factory object, Node. ListController is responsible for taking the data requested from and returned by the Node factory, and adds that data to the $scope object so that it is available to our Angular View. ListController also handles launching our dialog box, by interacting with our dialog box template, loadedNodeTemplate, which has been embedded in our View template wrapped in script tags with the attribute 'type=text/ng-template' specified in it.

Our entire app script, with whitespace for readability, is 27 lines long. How beautiful is that?