Directives continued

In this last directives section, we again return to our analogy (a team of laborers completing some building works), as we have yet to cover any decorative directives.

Rather than strictly being about styling and CSS, by decorative I mean in the sense of HTML elements being decorated with additional behaviour. This is like having a new kitchen fitted, which isn’t stuctural “bricks and mortar”, nor is it plumbing as the pipes have already been laid and are waiting to be used. But a new kitchen definitely gives a house additional behavior, especially if it has one of those fancy taps producing instant boiling water.

Lets now look at a couple of example decorative directives, namely ng-click and ng-checked.

ng-click

ng-click decorates HTML elements with the ability to invoke a model behaviour when they are clicked, which I suspect is fairly self-explanatory.

In the previous section, we introduced a factory service calculateCategoryPercentageand used it with a hard-coded list of articles. Let’s make our application a little more interesting by allowing new articles to be created.

Edit index.html as follows:

In the snippet above, we’ve added two new HTML elements. The text input element will update a model property newTitle via the ng-model directive (which has been discussed in a previous section on “Bindings”). The button element will invoke a model behaviour addArticle when clicked via ng-click.

Edit controllers.js as follows:

In the above snippet, addArticle uses $scope.newTitle when invoked to push a new article onto $scope.articles. Note that the new article has an empty categories array.

ng-checked

Our blog admin application needs to allow articles, new and old alike, to be re-categorized. Wouldn’t it be great if when this happens $scope.categoryPercentage is re-calculated? This can be accomplished with another usage of ng-click and a new directive ng-checked. Let’s explore how now.

Edit controllers.js as follows:

We’ve introduced quite a lot in the above snippet. Let’s discuss each in turn.

We previously mentioned that we would find another use for availableCategories. It is now also injected into articleCtrl and set as $scope.categories.

When we create bindings in our HTML (e.g., via ng-bind, {{ }}, ng-model and many others), we are implicitly creating watches. At a simplified level, whenever AngularJS detects something that might affect an application (be it a browser event, HTTP response, and many others), it checks all of its watches against their previous values and updates bindings if there is a difference. We use $scope.$watchto create a manual watch of the articles array and re-calculate$scope.categoryPercentage if there is a difference. The third parameter to $scope.$watchindicates that we want a “deep” watch. For more information, please have a look at this excellent StackOverflow answer.

Finally, we introduce two new model behaviors for interacting with an article’s categories. $scope.containsCategory simply saves us from having an ugly expression in our HTML. $scope.toggleCategory either adds or removes a category based on whether an article is already categorized as such.

To see all of this in action, edit index.html as follows:

In the above snippet, we come across the usage of ng-click and ng-checkedmentioned a little while ago. We’ve introduced a second ng-repeat loop to create a checkbox for each category. This ng-repeat loop is nested and, as such, creates the set of checkboxes for each article. ng-checked is used to ensure that the underlying state of the HTML checkbox element is kept in sync with the model (this is very similar to ng-model). ng-click is used to invoke toggleCategory with the appropriate article and category.

If you try this out, you should see the percentage of categories used being updated as you categorize articles.