Silverlight to Angular – 7 (Directives – I)

Even though I have Silverlight in the blog heading, we part ways with Silverlight in the last blog. Now, we are in Angular world. Ask any of the Angular user, which is their favorite feature in Angular? 99% of them will say, ‘Directives’. Without doubt, it is one of my favorite feature as well. In the beginning, when we started out with Angular, I mentioned that Angular can teach static HTML some new tricks like new elements, attributes etc., and it is done with Directives. It is without doubt that sets Angular apart from all the frameworks or toolsets.

I was going to create a step by step instruction for Hello Word directive but John Lindquist beat me to it. So instead of me doing ‘Hello World’, we will look at creating a directive for addition example we have been doing in couple of our examples. I am planning on doing it in three parts.

  1. Create a simple directive which shows static content.
  2. Create a simple directive which accepts input from HTML.
  3. Create a directive with binding and control hook up.

Lets start out by looking at our existing example;

View:

1 <!DOCTYPE html> 2 <html ng-app> 3 <head> 4 <title></title> 5 </head> 6 <body> 7 <div ng-controller="additionCtrl"> 8 First Number: <input ng-model="NumberOne"/><br/> 9 Second Number: <input ng-model="NumberTwo"/> 10 <button ng-click="add(NumberOne, NumberTwo)">Add</button><br/> 11 Result: {{Result}} 12 </div> 13 <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script> 14 <script src="Scripts/additionCtrl.js"></script> 15 </body> 16 </html>

Controller:

1 function additionCtrl($scope) { 2 $scope.NumberOne = 0; 3 $scope.NumberTwo = 0; 4 $scope.Result = 0; 5 $scope.add = function (a, b) { 6 $scope.Result = Number(a) + Number(b); 7 }; 8 }

In the current code, there are no directives, it nothing but our simple addition program. We are going to first modify our code to create a directive, similar to ‘Hello Word’ . This will be new directive which will create a new HTML element, which will display ‘First Number’. Similar to Filters, we will create directive at module level. Lets start by creating a simple directive;

Problem: Create a new HTML element, which will display ‘First Number’. Call the new element as ‘display-number’.

Lets start with the test so we know what are we trying to do and also learn the TDD along the way. I am still not very good at Javascript so I might be not be following some standard pattern, which I hope someone will help me correct.

Test:

Following our previous unit test model, we will create two folders, one for App and another one for Test so that we can separate the code that goes into production. We will copy the SpecRunner.html from our previous test project and which is similar to the following

1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 2 "http://www.w3.org/TR/html4/loose.dtd"> 3 <html> 4 <head> 5 <title>Jasmine Spec Runner</title> 6 7 <link rel="shortcut icon" type="image/png" href="http://searls.github.com/jasmine-all/jasmine_favicon.png"> 8 <link rel="stylesheet" type="text/css" href="C:\temp\jasmine\lib\jasmine-1.3.1\jasmine.css"> 9 <script type="text/javascript" src="http://searls.github.com/jasmine-all/jasmine-all-min.js"></script> 10 <script type="text/javascript" src="https://github.com/akiellor/rhino-hax/blob/master/modules/jasmine-console-runner.coffee"></script> 11 12 <script type="text/javascript" src="http://code.angularjs.org/1.0.6/angular.js"></script> 13 <script type="text/javascript" src="http://code.angularjs.org/1.0.6/angular-mocks.js"></script> 14 15 <!-- include source files here... --> 16 <script type="text/javascript" src="../App/scripts/additionCtrl.js"></script> 17 18 <!-- include spec files here... --> 19 <script type="text/javascript" src="spec/displayNumber.spec.js"></script> 20 </head> 21 22 <body> 23 </body> 24 </html>

Line (8) – Jasmine downloaded location will have the css file.

Line (16) – points to our controller where we will do addition of  two numbers.

Line (19) – This is where we are going to put our test spec.

Following code is our displayNumber.spec.js

1 describe('displayNumber', function () { 2 var element, scope; 3 4 beforeEach(module("additionApp")); 5 6 beforeEach(inject(function($rootScope, $compile) { 7 element = angular.element('<display-number></display-number>'); 8 scope = $rootScope; 9 $compile(element)(scope); 10 scope.$digest(); 11 })); 12 13 it('should create displayNumber span element', function () { 14 var elem = element.find("span"); 15 expect(elem.length).toEqual(1); 16 }); 17 18 it('should create displayNumber span element with "First Number" as text', function () { 19 debugger; 20 var elem = element.find("span"); 21 expect(elem[0].childNodes[0].data).toEqual("First Number"); 22 }); 23 });

Line (4) – When testing directives, we will create an app and we load that every time before each test.

Line (6-11) are the ones required to generate the directive into HTML span element.

Line (6) – Will talk about compiling and linking little bit later, but for now, we inject rootScope and compile from angular mock to our test.

Line (7) – <display-number> is our directive under test. So we create an element with that directive as if it appears in the view.

Line (9) – Compile will walk through the HTML and create list of angular directives available.

Line (10) – $digest triggers the process converts the all the directives to proper HTML tag and generate associated watch etc.,

Line (13 – 16) is our first test to make sure there is span tag created after directive processed.

Line (18 – 22) is our second test, which make sure the text ‘First Number’ shows up in the Span.

If you would run, the specRunner.html, both the tests will fail.

View with the directive.

You can find the solution in jsFiddle.

1 <div ng-controller="additionCtrl"> 2 <display-number></display-number>: <input ng-model="NumberOne"/><br/> 3 Second Number: <input ng-model="NumberTwo"/> 4 <button ng-click="add(NumberOne, NumberTwo)">Add</button><br/> 5 Result: {{Result}} 6 </div>

Line (2), you will see a new HTML tag <display-number>, this is the part which makes Angular awesome. We literally created a new HTML tag for the purpose of our coding. Even though other frameworks allow you to do something similar, you can not create new HTML element like Angular does. Couple of things to remember;

  1. Make sure ng-app is there in the HTML tag pointing to app module.
  2. Always make sure, when you create an directive element, always put closing element. Don’t use inline closing like <display-number />

Here is the controller with directive

1 var app = angular.module("additionApp", []); 2 3 app.directive("displayNumber", function () { 4 return { 5 restrict: 'E', 6 template: '<span>First Number</span>' 7 }; 8 }); 9 10 function additionCtrl($scope) { 11 $scope.NumberOne = 0; 12 $scope.NumberTwo = 0; 13 $scope.Result = 0; 14 $scope.add = function (a, b) { 15 $scope.Result = Number(a) + Number(b); 16 }; 17 }

Line (1) when using directives, similar to Filters, you need to have an app associated with it.

Line (3) sets up the directive. If you notice the method name is ‘displayNumber’ (camel case). But when you use the directive you use display-number.

Line (4) there are couple of ways, you can create directives and we are following a simple approach where it return a class for directives.

Line (5) ‘restrict’ specifies, is the directive an ‘Element’ (E), Attribute inside an element (A), Class (C) or Command (M). In our case we are creating a new element called display-number so we are restricting it to an Element. The default is Attribute.

Line (6) provides the template that need to be used in place of the directive. In this case, it is nothing but straight forward span element.

Rest of the code is our old controller. Next we will look at how to pass in values to

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s