Chapter 2 - AngularJS As a UI Component Framework

AngularJS is a versatile client-side MVC/VM framework since it provides pattern implementations for both controllers and view-models (IMO). It provides most of the structural features necessary for constructing the proverbial single page app (SPA) including client routing, templating, data modeling, app configuration, logic containers and service hooks for interacting with the outside world via AJAX or REST. The AngularJS team is adding more application features with every release, and there are already several books about building browser apps with AngularJS on the market.

The goal here is not to be yet another book about how to build a web app, but rather to discuss some AngularJS “goodies” that transcend the above list of app framework features offered by most client-side frameworks that allow us to construct dynamic, interactive UI components that can be used and reused across contexts, pages, applications, and organizations.

To illustrate, suppose you are a web developer or designer for a large enterprise working on a specific micro-site or application, you likely will have many corporate standards and constraints that must be adhered to such as style guides, uniform look-and-feel, legacy APIs, etc. as part of the development process. Including a simple, standard component such as a search bar or field in a page, can become quite complex when considering what CSS rules to apply plus extra JavaScript for interaction behavior, offering auto-complete, URLs to talk to, error message display, and so on in a way that adheres to corporate standards for consistent styling and behavior. This task can easily result in the creation of hundreds of lines of HTML, CSS, and JavaScript boiler plate code plus many developer hours to implement.

On the other hand, what if the task could be so easy as adding a single, custom HTML tag in a page such as <search-bar></search-bar>, and any special configuration could be achieved via custom attributes such as <search-bar color=”blue” autocomplete=”false”>. Entire pages and new apps could be built quickly out of such custom UI components for headers, footers, navigation, sign-in widgets and more with simple, declarative markup. Building large parts of a website that adheres to enterprise style guidelines could be as easy as loading a component pallet in your browser, making some configuration choices, and including the custom HTML tag with attribute configurations in your markup.

Developers can be freed to focus on the application to be built rather than all the page boilerplate, code bases can be drastically shrunk, and development time can be drastically reduced. In fact, AngularJS was initially conceived and developed in 2009 by Google employee, Miško Hevery, for just this purpose.

In AngularJS, custom, declarative markup that dynamically extends the static nature of HTML, such as our <search-bar> example above, lies at the core of the framework. AngularJS terms these as directives. Angular directives can be used for all kinds of dynamic UI behavior. The framework comes with many built in such as ng-click for handling logic upon a mouse click event. But the framework also gives you the ability to define your own, and this becomes a handy tool for encapsulating our own units of business logic with visual representation. In fact, a directive can include an inline HTML template with CSS along with its own controller and data scope.

This is very similar to the idea behind the emerging W3C Web Components standards which are still a ways away. However, we can emulate a web component using AngularJS directives, and the AngularJS team intends to evolve the framework to converge with the Web Components standards.

We will discuss both of the above extensively in this book, but first we need to step back a bit and start with a good foundation of the design features and goals of AngularJS for proper context.

Why AngularJS? (part 2)

What advantages does it have over Backbone.js or YUI or Extjs? Why would I want to switch? What’s the learning curve like, and so on.

These are some of the first questions many UI developers who have been coding in jQuery or other imperative frameworks ask. The creators of the various popular frameworks in use all have opinions as to why their approach is best. Some think that JavaScript is not a real language and the developer needs to be protected from it. Some think that JavaScript should only be an optional addition to a web page. Some think that object oriented programming is the best programming paradigm, so they add all kinds of faux OO to JavaScript, and some are very opinionated about having no “opinion” at all.

In the past, some of these opinions held more weight than they do now. In 2005 a large percentage of web surfers kept their Javascript disabled, so it did not make sense to create pages that were dependent on it for proper functioning. “Progressive enhancement” or “graceful degradation” were important, so non-obtrusive, imperative frameworks like jQuery and Prototype made sense. The Javascript (behavior) should be entirely separate from the content (html). If Javascript were turned off, the surfer would have, at worst, a degraded but functional experience.

This is not true today. JavaScript can still be turned off in browsers, but it is now accepted as safe, ubiquitous, and necessary for browsing any modern website.

The AngularJS team has some strong opinions as to why their approach is best and why you’d want to do things the “Angular way”. From the landing page of Angularjs.org:

“HTML is great for declaring static documents, but it falters when we try to use it for declaring dynamic views in web-applications. AngularJS lets you extend HTML vocabulary for your application. The resulting environment is extraordinarily expressive, readable, and quick to develop.”

Dynamic Views

“Other frameworks deal with HTML’s shortcomings by either abstracting away HTML, CSS, and/or JavaScript or by providing an imperative way for manipulating the DOM. Neither of these address the root problem that HTML was not designed for dynamic views.”

This argument would have been quite a stretch in 2005, but it actually makes sense today. The slow evolution of HTML has not kept pace with the way we use the Web. Today the web is highly interactive, and waiting for the server to respond to each interaction is only still seen in some big enterprise, intranet applications that handle payroll, HR, purchasing and other corporate applications.

The fact is that any web UI developer spends an inordinate amount of time writing code that queries a DOM element, attaches an event listener to it, and manipulates it in response. Much of these dynamic behaviors are now ubiquitous across the web and organizations, including UI widgets like carousels, modals, transitions, tab panels, etc.

Ideally we should be able to include these widgets declaratively via an HTML element such as <carousel>, and these elements should be defined, configurable, and extensible as part of the HTML specification, rather than worrying about including extra JavaScript and CSS.

Along the same lines, representing or modeling the application’s data in the client also requires a lot of boilerplate code. This includes adding custom event listeners and handlers for changes in a data object, the belief that model objects should adhere to object oriented paradigms including inheritance, interfaces, getters, setters, etc. For a change in a datum to be reflected in the view, and visa versa there is a lot to wire up programmatically, and a lot to keep track of. Conceptually, the current (jQuery) way of managing dynamic behavior requires maintaining two parallel tree structures of information, one for HTML and one for imperative DOM manipulation.

Including dynamic DOM behavior declaratively as HTML markup is nothing new. Dojo and Bootstrap’s widgets have given the page author the option of declarative inclusion for quite some time.

Two Way Data Binding

The AngularJS team is of the opinion that data-binding between model and view should be intrinsic. Again, from the AngularJS.org landing page:

“Data-binding is an automatic way of updating the view whenever the model changes, as well as updating the model whenever the view changes. This is awesome because it eliminates DOM manipulation from the list of things you have to worry about. …Controllers are the behavior behind the DOM elements. AngularJS lets you express the behavior in a clean readable form without the usual boilerplate of updating the DOM, registering callbacks or watching model changes. …Unlike other frameworks, there is no need to inherit from proprietary types; to wrap the model in accessor methods. Just plain old JavaScript here. This makes your code easy to test, maintain, reuse, and again free from boilerplate.”

Behind the two way data binding is the VM or view-model component of MVVM. The view-model is the object representing the state of both the application data and the views representation of it. The flow is bi-directional. A change on one side necessitates a change on the other automatically.

The VM in AngularJS is represented by the $scope object which the framework automatically injects into controller functions as a parameter.

2.0 AngularJS 2 way data binding


// any variable attached to $scope can have live representation in the view
myApp.controller('myController', function($scope){
    $scope.myAppModel = {
        firstName: 'Dave',
        occupation: 'UI Architect
    };
});

<!-- if we type in new values, they will display in real time -->
<div ng-controller="myController">
  <span> {{ myAppModel.firstName }} </span><br>
  <input type="text" name="firstName" ng-model="myAppModel.firstName">
  <span> {{ myAppModel.occupation}} </span><br>
  <input type="text" name="occupation" ng-model="myAppModel.occupation">
</div>

AngularJS currently accomplishes (view-model) data-binding via “dirty-checking”, a process of recursively comparing current data values for each $scope object with previous values to see what has changed if there has been an associated UI event or “watched” model event.

This can be an expensive process in the currently implemented version of JavaScript (ECMA5). However, the need for “model driven” views (MDV) is well recognized, and the next version of JavaScript will support Object.observe() natively, and AngularJS will naturally take advantage of this.

The concept of AngularJS $scope is an important and complex topic, so we will examine it in quite a bit more depth in chapter 4.

Dependency Injection

Speaking of injection, AngularJS encourages a lot of dependency injection (DI), a programming paradigm made popular by the Java Spring Framework several years ago.

Any application, module, or function in JavaScript can resolve dependencies as variables from within that function’s scope, a parent scope or the global scope. Direct dependencies outside a function’s immediate scope are bad, and global dependencies are considered evil among experienced JavaScript developers and especially the AngularJS team- not just due to global namespace pollution and the potential for naming collisions and overwrites, but because a tight coupling between a function or module dependency and a global variable make its quite hard to write valid unit tests.

Test Driven Development (TDD)

By building a framework around the idea of explicitly injecting dependencies, the AngularJS team made it quite easy for the developer to isolate individual units of code. Additionally, AngularJS is shipped with several configurable mock services for mimicking dependencies outside the framework such as AJAX calls and results, and on top of that the “official” AngularJS tutorial and the Angular-seed project on GitHub include an integrated testing environment using Jasmine and Karma for unit and end-to-end testing respectively.

https://github.com/angular/angular-seed

http://docs.angularjs.org/tutorial

So AngularJS developers are highly encouraged to create unit tests for their functions as they write them, and there really is no excuse for not doing so. This is especially true when building reusable UI components that may be consumed by 3rd parties. It is critical that the APIs of the components we create maintain consistent behavior as the internals of the component are developed from one version to the next.

If JavaScript were a compiled language, the compiler would catch many bugs for us. But JavaScript is a dynamically typed, interpreted language, so comprehensive unit and e2e testing is our first line of defense.

Don’t Repeat Yourself (DRY)

In the AngularJS world, EVERYTHING is a module. If you are familiar with asynchronous dependency loaders like Require.js, and how they allow us to include library and application dependencies without having to worry about when and where they come from, then AngularJS modules should be easy to understand.

The AngularJS team, like the rest of us, hates to repeat the same blocks of code needlessly. Like a lot of the jQuery binding boilerplate, repeated code is a large contributor to the problem of code bloat these days. That’s why they decided that all AngularJS code must be encapsulated as AngularJS modules. AngularJS modules can be defined in any order, include dependency modules that haven’t been defined yet, and still work properly. The only prerequisite is that the Angular.js core is loaded first.

Just like AMD loaders, AngularJS registers all module definitions before resolving dependencies in the module definitions. So there is no concern about the order that your AngularJS module dependency files are loaded into the browser, as long as, they are loaded after angular.js and before the DOMContentLoaded event when AngularJS normally bootstraps. This, of course, solves the problem of having to repeat blocks of code in order to satisfy the dependency on that code in different modules.

Repeated JavaScript code that finds its way into the browser or codebase repository is a pervasive problem in larger enterprise organizations where front-end development may take place among different groups spread out over continents, and the AngularJS module system can be quite helpful in avoiding or preventing this type of bloat.

2.1 AngularJS Asynchronous Module Definition and Retrieval


// Define an AngularJS module with dependencies referenced by "MyApp".
// Dependencies can be loaded in any order before the "bootstrap" process and
// AngularJS takes care of resolving in the correct order.
angular.module('MyApp', ['MyServiceDeps', 'ThirdPartyPlugin', 'AnotherDep']);

// A module with no dependencies is created like so
angular.module('MyApp', []);

// We can then retrieve the module for configuration like so:
angular.module('MyApp');
// Notice the only difference is the presence or absence of an array as the 2nd 
// argument.

POJO Models

It amazes me how many UI developer job descriptions I see that require someone to be skilled in “object oriented” JavaScript when JavaScript was never really meant to be an “object oriented” language like Java or C# to begin with. It’s safe to say that most JavaScript frameworks out there have some sort of system for emulating Java style object inheritance hierarchies using JavaScript’s prototype object chains, and many try to emulate object encapsulation with unnecessary “getter” and “setter” functions that don’t really prevent direct access. This is another source of needless boilerplate bloat due to a general lack of understanding of the JavaScript language by traditional application engineers and engineering managers.

POJO is an acronym for plain old JavaScript meaning no getter/setter, or inheritance boilerplate- just functions and mix-ins. The AngularJS team’s definition of MVC uses just plain JavaScript objects for its models, a conscious decision to again minimize bloat and complexity while isolating functionality for the sake of both testability and maintainability.

Below is a contrived comparison of a Backbone.js model to an AngularJS model. Backbone.js, like a lot of other frameworks, requires an inheritance hierarchy and encapsulates the model properties behind getters and setters to mimic the style and syntax of classical object oriented languages like Java. This is a natural result of engineers trained on Java, Ruby, or C++ who’ve later switched to JavaScript but haven’t been able to fully embrace the different programing paradigms. JavaScript is terse. Most common functionality can be directly mixed in to the object that needs it. Objects can be instantiated with simple object literal syntax, and object attributes can be accessed and modified directly. For the sake of source code reduction, the AngularJS team has forgone all the unnecessary OO syntactic sugar.

2.2 Example of AngularJS and Backbone.js Models


// Backbone.js model
// step 1 - extend the base Model class
var MyModelConstructor = Backbone.Model.extend({});
// step 2 - create a new model instance
var myModelInstance = new MyModelConstructor();
// step 3 - add model attributes
myModelInstance.set({ 
    firstName: 'Dave',
    lastName: 'Shapiro',
    title: 'UI architect'
}); 
// step 4 - manually register attribute listeners with change callbacks
// to update the DOM 
myModelInstance.on('change:title', function(model, title){
    // callback function for a jQuery style DOM update
});

/*******************************************/

// AngularJS model
// no required boilerplate including set, get, or extend
// no event listeners, changes reflected in DOM automatically
// simple object literal notation
$scope.myModel = {
    firstName: 'Dave',
    lastName: 'Shapiro',
    title: 'UI architect'
};

AngularJS and Other Frameworks in the Same Page

As mentioned before, all JavaScript frameworks are opinionated even if the opinion is to be un-opinionated. Some JavaScript frameworks are opinionated to the point of bordering on self-righteousness. The authors are so convinced that their way is the “right” way they cannot conceive as to why a web developer would need or want to make use of any other toolkit or framework. These frameworks typically allow only one instance to be attached to the root of the page in a monolithic, all or none fashion, and happily generate massive amounts of DOM elements with inline CSS in such a fashion that other tools cannot access. I won’t name any names here, but many web developers in larger enterprise organizations know who I’m talking about.

AngularJS, on the other hand, is both non-exclusive and non-intrusive when loaded in the same page as other frameworks. While it is often “bootstrapped” in declarative fashion with ng-app in the <html> or <body> tag when used as a single page app framework, the ng-app directive can also be placed in the DOM hierarchy at any level. Alternatively, AngularJS can be bootstrapped in the page imperatively at any level of the DOM, more than once, and at any point of the page life cycle without affecting code outside above its level in the DOM.

2.3 AngularJS Application Bootstrap Options


<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html" ng-app="DeclarativeApp">
<head>
    <meta charset="utf-8">
    <title>AngularJS Bootstrapping Demo</title>
</head>
<body>

<!-- do this to run an AngularApp anywhere in the page -->
<div id="imperative_app">
  ...
</div>

<script>
// on DOM ready find a DOM node and start an AngularJS app
angular.element(document).ready(function () {
    angular.bootstrap('#imperative_app', ["ImperativeApp"]);
});
</script>
</body>

This allows the developer the flexibility to use AngularJS as both a single page app (SPA) framework, or as a framework to support declarative UI components within a page that may or may not be using another framework for SPA functionality such as Backbone.js. The latter is, or course, the subject of this book.

Forward Compatibility with W3C Web Components and MDV

This is what makes AngularJS particularly attractive to this author. The W3C Web Component specifications when fully implemented by the major browsers will most certainly cause a major paradigm shift in the way web page construction is viewed.

Building websites and web apps will no longer be seen as a monolithic engineering endeavor. Creating a web app and adding functionality, will become more of a process of adding and extending off-the-shelf UI components represented as HTML markup. The Web Component specs describe how we will be able to encapsulate and scope JavaScript, CSS, with an HTML fragment and have it load, attach/detach and activate/deactivate at any point in the page lifecycle. Also, when Object.observe() is made available as the major browsers implement the next version of ECMA Script, model driven views (MDV) will become a basic part of any web page functionality.

2.4 Some of the new web components goodies


<!-- import html directly into a page -->
<link rel="import" href="include.html">

<!-- the new template tag -->
<template id="commentTemplate">
   <!-- innert DOM -->
   <style> /* scoped css rules */ </style>
   <div></div>
</template>

<!-- extend existing or define new elements -->
<element extends="button" name="big-button">
    <script>
        // custom element script API
    </script>
</element>        

<script>
    // create a "light weight" iframe
    var s = element.createShadowRoot();
    var t = document.querySelector('#commentTemplate');
    r.appendChild(t.content.cloneNode(true));
    // all object will have observe() methods to facilitate 
    // model driven views
    Object.observe(callback);
</script>    

However, in the snail’s pace of web standards development, full Web Component and MDV support may still be a ways off, but AngularJS directives along with AngularJS data binding (the VM of MVVM) allow us to emulate this type of component encapsulation and functionality now. What’s even better is that the AngularJS team will actively evolve the framework toward full Web Component and MDV compatibility and usage. UI component code written in AngularJS will likely be quite a bit more forward compatible with evolving web standards than components created using Extjs or jQuery UI. This translates to a longer shelf life and easier upgrade path for the code we write today. What UI developer is ok with their code becoming obsolete after a year or two?

Summary

In this chapter we discussed some of the major attributes of AngularJS that are making it quite popular and widely adopted by the web development community including:

  • dynamic views
  • code reduction
  • automatic data binding
  • dependency injection
  • TDD friendly architecture
  • POJO as the framework’s controllers
  • non-intrusiveness or exclusiveness
  • Web Component and MDV forward compatibility

In the next chapter we will explore the mechanics of AngularJS core directives, as well as rolling our own custom directives with a focus on component encapsulation.