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">
    <td>{{video.Title}}</td>
    <td>{{video.Length}}</td>
    <td>
        <button ng-click="editVideo(video.Id)">Edit</button>
        <button ng-click="deleteVideo(video.Id)">Delete</button>
    </td>
</tr>

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.

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) {
    $window.alert(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.


Comments
gravatar Mike Sheehan Monday, February 25, 2013
Hi Scott, This has been a great series - thank you! I just discovered AngularJS this weekend and found your tie-in to WebAPI very helpful. I second the other comments and would really like to see a Pluralsight course on the topic. What is your opinion on having the AngularJS / HTML in the same project as the WebAPI services? I've been using .NET MVC for the past year or so and just learning about WebAPI and not sure the benefits / pitfalls of separating the front-end in a separate project from the services. Thanks again!
gravatar Rob Conery Monday, February 25, 2013
Hi Scott - nice article. I'm sure this is coming in a later post but I thought I would ask anyway (since I'm in the planning stages of doing an Angular thing for Tekpub... wish it could be with you :):):) but... You can keep this stuff completely out of the UI using directives. I'm sure you probably know this - but was curious if you were planning on adding? Also the data tag in the TR is probably not necessary, and passing the ID instead of the video itself is also a bit too granular perhaps?
gravatar Scott Monday, February 25, 2013
@Mike: I'd say that's a grey area. If you are building a Web API for other clients (not just your own front-end), that's a compelling reason to keep the services separate. If you are into SOA, DDD modules, team makeup, those are all factors, too. For web APIs with a single client and you write both the front-end and the service I don't see a reason to keep them separate.
gravatar Scott Allen Monday, February 25, 2013
@Rob: Thanks! Always dangerous to talk about what I'll post in the future (since I may never get around to it), but yes, I have a grandiose vision of this series progressing through Angular like I did, and in learning to do new things replace code with custom directives, replace low level $http work with $resource, etc. Would be easier to do in a video :) Good catch on the data- tag in the TR. Forgot to remove that.
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!