OdeToCode IC Logo

Expressions In AngularJS

Monday, February 25, 2013

The last feature I need to add for the video sample is the ability to edit and delete videos. One approach with Angular is to bind edit and delete button click events to the model using the ng-click click directive, which we've seen before.

<tr ng-repeat="video in videos">
        <button ng-click="editVideo(video.Id)">Edit</button>
        <button ng-click="deleteVideo(video.Id)">Delete</button>

What's new in the two usages of ng-click here is the complexity of the expression inside. More on expressions later, but ultimately the expression will invoke delete and edit methods with parameters, where the delete method can look like the following:

$scope.deleteVideo = function (id) {
    $http.delete(serviceUrl + "/" + id)
         .success(function () {
             $scope.videos = _.reject($scope.videos, videoFinder(id));

This code is using Underscore's reject to help update the model.

All the HTML and JavaScript for the sample is in a gist, and if you followed (closely) the original course at Pluralsight, you might remember the original sample was around 120 lines of script with jQuery and Handlebars. The same features in Angular require around 60 lines of script. However, we've only scratched the surface of what we can do with AnjularJS. Not only can we write less code, we can add some new features using additional Angular APIs, and still have a testable controller.

We'll look at some of these additional features in the future. For now I wanted to return to the ng-click expressions.


At first glance, ng-click looks similar to writing JavaScript code inside an onclick attribute, which we've learned to avoid for many good reasons. However, the resemblance is only superficial. An ng-click directive doesn't prevent progressive enhancement or go against the true spirit of unobtrusive JavaScript. I think of ng directives and other {{expressions}} as natural extensions to the declarative language of HTML. Angular uses a compiler service at runtime to process the directives and construct linkages between the HTML view and a model.

Another significant difference between ng-click and onclick is the execution context. Code inside an onclick attribute executes against the global window object, while an expression inside of ng-click executes against a specific scope object, typically the scope object representing the model for the current controller.

In other words, if you wanted to invoke the global alert function in JavaScript, the following onclick handler will work:

<button onclick="alert('hello!')">Say Hi</button>

However, the following ng-click code will not invoke the global alert function:

<button ng-click="alert('hello!')">Say Hi</button>

The only way for the code inside the directive above to work is to have an alert method defined for the current scope.

$scope.alert = function(message) {

In this example we'll forward the call to the global alert function by using a well known $window service that is injected into the controller (which is a testable approach to using the window object).

The code in the ng-click attribute for the edit and delete buttons actually executes against a scope defined by the ng-repeat directive. The ng-repeat directive is providing a video for every object in the videos collection, and the repeat scope prototypically inherits from the controller scope (which is why both deleteVideo from the controller scope and video.Id from the repeat scope are available to use in the expression). For more details on how scopes in Angular work, see "The Nuances of Scope Prototypal Inheritance". Scopes are important to understand.

One final difference between ng-click and onclick is that the expression syntax supported by ng-click is like a subset of JavaScript. You can make method calls, use a ! operator, and even perform an assignment (which is frowned upon), but you cannot use flow control statements, among other things. Remember the view is supposed to be simple, so expressions typically reference a property or invoke a method to keep most of the logic inside a controller.