AngularJS Abstractions: Scope

The parameter to the controller function is named $scope.

var AboutController = function($scope) {
 
    // ...    
 
};

The name $scope is important since it allows the AngularJS dependency injector to know what type of object you are asking for, in this case an object that will contain the view model. Any plain old JavaScript you attach to $scope (properties, functions, objects, arrays) is eligible to use from the expressions inside the view.

In the last post our model was relatively “flat” in the sense that properties and functions were added directly to $scope:

$scope.rabbitCount = 2;
 
$scope.increase = function() {
    $scope.rabbitCount *= $scope.rabbitCount;
};

This allowed us to use rabbitCount and increase() in the markup that was inside the view scope (the div) of the AboutController:

<div data-ng-controller="AboutController">
 
    <div>Number of rabbits in the yard: {{rabbitCount}}</div>
 
    <button ng-click="increase()">More rabbits</button>
     
</div>

You can think of $scope as the execution context for the expressions in the view. Saying ng-click=”increase” results in a call to $scope.increase. The only thing tricky to understand about $scope is that having a controller inside a controller, or a controller inside an application (which you’ll always have), will result in nested $scopes, and a nested $scope will prototypally inherit from it’s parent scope by default. This is why $scope is injected by angularJS – the framework sets up the prototype chain before giving your controller the $scope object to use as a model.

Inheritance means a view has access to it’s own scope as well as any inherited scope. In the following example, the view inside the ChildController markup can use an expression like {{rabbitCount}}, and this expression will read the rabbitCount property of AboutController’s scope ($scope.rabbitCount will follow the prototype chain).

<div data-ng-controller="AboutController">    
 
    <div>Number of rabbits in the yard: {{rabbitCount}}</div>
     
    <div data-ng-controller="ChildController">
        Number of rabbits in the yard: {{rabbitCount}}
        Number of squirrels in the yard: {{squirrelCount}}
    </div>
 
</div>

The one place to be careful with the inherited scope is with 2 way data binding. The way JavaScript prototypes work is that writing to the rabbitCount property of the ChildController $scope will add a rabbitCount property to the ChildController $scope and effectively hide the parent property. $scope.rabbitCount no longer needs to follow the prototype chain to find a value. More details and pictures on this scenario in “The Nuances of Scope Prototypal Inheritance”.

AngularJS Abstractions: Filters

Filters in AngularJS can form a pipeline to format and adapt model data inside a view. The built-in filters for AngularJS are date (format a date), filter (select a subset of an array), currency (format a number using a currency symbol for the current locale), and many more. I say filters can form a pipeline because you place them inside expressions in a view using a pipe symbol. The canonical example of a filter is the filter filter, which in the following markup filters a list of items according to what the user types in the search input (try it live here).

<input type="search" placeholder="Filter" ng-model="searchTerm"/>    
 
<ul ng-repeat="item in items | filter:searchTerm | limitTo:maxItems">
    <li>{{item}}</li>
</ul>

Notice the output of filter is also piped to a limitTo filter, which restricts the number of items in the list to maxItems (where maxItems would need to be defined in the model).

Custom Filters

Custom filters only require a filter function to be registered with Angular. The filter function returns a function that will perform the actual work. For example, a plurify filter to add the letter s to the output:

(function() {
 
    var plurify = function() {
        return function(value) {
            return value + "s";
        };
    };
 
    angular.module("patientApp.Filters")
           .filter("plurify", plurify);
}());

Now plurify is available to use in a view:

<ul ng-repeat="item in items | filter:searchTerm | limitTo:maxItems">
    <li>{{item | plurify }}</li>
</ul>

A filter can also take one or more optional parameters.

var plurify = function() {
    return function(value, strength) {          
       strength = strength || 1;
       return value + Array(strength + 1).join("s");
    };
};

Now we can add 5 s characters to the output:

<li>{{item | plurify:5 }}</li>

The beauty of filters is how you can package up small pieces of testable code to make the job of the model and view a bit easier, and the syntax to use a filter is easy and intuitive.

AngularJS Abstractions: Directives

Directives are one of the lynchpins that make AngularJS work, and also make the framework nice to work with.

Directives are the attributes Angular searches for in the markup when the DOM is loaded, attributes like ng-app, ng-controller, and ng-click. You can also use directives to create custom elements, like <my-widget>, but doing so depends on how much value you place in having your raw source validated by an HTML validator.

If validation is high on your list, there are HTML 5 compliant approaches to using directives, including data- prefixing attribute directives (use data-ng-controller instead of ng-controller, for example, and Angular still works).

What Are They Really?

From an abstraction perspective, directives form an anti-corruption layer between a model and a view. Directives are where you can listen for changes in a model value and then go manipulate the DOM. Directives allow models to change the content and presentation of a view without the model getting bogged down by the DOM manipulation code. The model stays clean, testable, and plain.

Here’s an example of a custom “myShow” directive(note that AngularJS already has an ngShow directive to do this job, but this is an easy example to understand).

The purpose of the myShow attribute is to show or hide a DOM element based on the truthiness of a model value.

First, the markup:

<div data-ng-controller="TestController">
     
    <div data-my-show="model.visible">
        {{model.message}}
    </div>
             
    <button data-ng-click="model.toggleVisible()">Click me!</button>
 
</div>

Notice the data-my-show attribute on the first inner div. The attribute value is set to “model.visible”.

Here is the controller and model for the scenario:

(function () {
 
    var model = {
        visible: false,
        message: "Surprise!",
        toggleVisible: function () {
            this.visible = !this.visible;
        }
    };
 
    var TestController = function ($scope) {
        $scope.model = model;
    };
 
    TestController.$inject = ["$scope"];
 
    angular.module("testApp")
           .controller("TestController", TestController);
 
}());

All that’s needed now is the code for the my-show directive:

(function () {
 
    var myShow = function() {
        return {
            restrict: "A",
            link: function(scope, element, attrs) {
                scope.$watch(attrs.myShow, function(value) {
                    element.css("display", value ? "" : "none");
                });
            }
        };
    };
 
    angular.module("testApp")
           .directive("myShow", myShow);
}());

The naming conventions applied by AngularJS allow the directive registered as “myShow” to be applied in markup as data-my-show, as well as my-show and a few other variations. 

The directive itself is setup by a function that returns a “directive definition object” (let’s call it the DDO). The DDO has to have a few members in place for the directive to work, but this sample only uses a couple members: restrict and link.

The restrict property specifies where the directive is valid (E for element, A for attribute, C for class, M for comment). Since the default for restrict is “A”, this property could be omitted.

The link function is responsible for making things happen by setting up watches to listen for changes in the model.  Angular invokes the link function and gives it the model (scope), the element (effectively a jQuery wrapped reference to the DOM element), and an attrs parameter containing the attributes of the element. Asking for attrs.myShow will return “model.visible”, since that is the attribute value specified in the markup.

The link function in this example uses scope.$watch to listen for changes in the specified model value. $watch is a good topic for a future post, but for now we can say if the model changes it’s visible property from false to true, the framework invokes the listener function passed as the second parameter to $watch.

The listener function in this sample sets the display property of the element to an empty string or “none” to toggle visibility of the element. The value parameter in the function is the recently changed model value, the value the directive set up to $watch.

Where Are We?

This sample doesn’t show the full capabilities of a directive, which can also load additional markup from a string or a URL, as well as other nifty features.  We’ll cycle back to more advanced features in a future post.

Although custom directives can require  more code in a project compared to using DOM manipulation inside a controller, the advantage of having a directive in place is in keeping the models and controllers simple. Plus, once written, most directives can be parameterized and reused in different places and multiple applications.