Introduction
In this article, we will try to understand the basic concept of directives in Angular. We will look at how directives are used in Angular and try to use some built in directives in Angular.
Link to complete series:
- Angular Tutorial - Part 1: Introduction to Angular.js
- Angular Tutorial - Part 2: Understanding Modules and Controllers
- Angular Tutorial - Part 3: Understanding and Using Directives
- Angular Tutorial - Part 4: Understanding and Implementing Filters
- Angular Tutorial - Part 5: Understanding and Implementing Services
- Angular Tutorial - Part 6: Building and Validating Data Entry Forms
- Angular Tutorial - Part 7: Understanding Single Page Applications and Angular Routing
Background
Traditionally, web only consisted of static HTML pages linked with each other. Rise of server side technologies enabled us to create the HTML dynamically on the server side and push it back to the client. But in both cases, the generated HTML contains the elements and attributes that are defined in the HTML specifications. With directives, Angular has taken things to the next level.
What Angular directives provide us is that they let us write our own HTML elements and attributes. This would mean that now we have a possibility of extending the HTML vocabulary and define our own logic and behaviour in the form of HTML-like elements and attributes. We can create directives to custom elements, attributes and even HTML comments.
Using the Code
The topic of directives in Angular can be broken into two parts. First, we can talk about the built in directives provided by Angular and how we can use them in our application. Second, how can we create a custom directive ourselves to create a reusable piece of functionality in the form of a directive.
Built in Directives in Angular
In this article, we will be talking about the most commonly used built in Angular directives. Creating custom directives is a fairly advanced topic and we will talk about that in the 9th article of the series as by then, we must have looked at other aspects of Angular and find ourselves in much better position to dive into details of creating custom directives. So let's get started with the built in directives in Angular one by one.
ng-app
In the previous articles of this series, I have mentioned that we need to put ng-app
in the HTML element where we want to use the Angular app. What is this ng-app
? ng-app
is a built in Angular directive. What ng-app
directive does is that it will define the root of an Angular application.
The element on which it is defined, all the elements contained within that element are then a part of the Angular app. Usually, we put it on the HTML element itself so that the complete HTML page becomes a part of the Angular application. It is possible to just write ng-app
without any parameter and this will bootstrap the Angular application in automatic mode. However, it is not a good idea to do so as it is little tricky to manage dependencies and specify the starting point of the application if we do this.
Instead, what we should do is that we should pass the Angular module name that should be used for this page. So if we have a module defined as myAngularApplication
. Then the ng-app directive should look like the following:
What this above code will do is that, it will look at the ng-app
directive and initialize the Angular application. It will then look for the Angular module myAngularApplication
and treat it as the starting point of the application. Having the ng-app
directive manually bootstrap the application is beneficial as it gives us a chance to specify the module that can be treated as the entry point and all the components (controllers, services, filters, etc.) that depend on the specified modules can be used easily.
ng-controller
We have also looked at the ng-controller
directive in the previous article. Whenever we want any view to be associated with any controller, we can attach the controller to the view using the ngController
directive.
ngController
directive attaches the view with the controller defined in the directive. So in the above code, the booksController
will get associated with the body of this HTML document. The HTML element that contains the ngController
directive can then access all the properties defined in the controller. The {{message}}
that we are accessing in the above code, if it's defined in the booksController
, the value of the message
property will be shown here. Let's look at the controller code to see how the message
property is defined on $scope
in our controller.
(function () {
var booksController = function ($scope) {
$scope.message = "Hello from booksController";
this.greeting = "This is a greeting message using controller as syntax";
$scope.books = [];
$scope.fetchBooks = function () {
$scope.books = [
{ ID: 1, BookName: "Test Books 1", AuthorName: "Test Author 1", ISBN: "TEST1" },
{ ID: 2, BookName: "Test Books 2", AuthorName: "Test Author 2", ISBN: "TEST2" },
{ ID: 3, BookName: "Test Books 3", AuthorName: "Test Author 3", ISBN: "TEST3" },
{ ID: 4, BookName: "Test Books 4", AuthorName: "Test Author 4", ISBN: "TEST4" },
{ ID: 5, BookName: "Test Books 5", AuthorName: "Test Author 5", ISBN: "TEST5" }
];
}
}
angular.module('myAngularApplication').controller('booksController', ["$scope", booksController]);
}());
We have talked about the controller as syntax briefly in the previous article. We can use the ngController
directive to also let the view use the controller code using the controller as syntax. What "controller as
" syntax does is that it will let the view access all the properties defined on the controller object.
So what the above code will do is that it will show use the greeting
property of the controller and show its value in the view. Using this approach would mean that we don't have to push the values in the $scope
but rather we can have controller have the properties and using controller as syntax, the view should be able to use it.
Note: There are scenarios where we don't have to explicitly use the ngController
directive explicitly in a view to bind it with the controller. One example is while using routing, we can attach the controller with a view in the route definition. But we will look at that in detail in later articles that how Single Page Applications where we use $route
or uiRouter
don't need the ngController
directive in the view. But for non-SPAs, ngController
is the way to attach the view with the controller.
ng-init
ngInit
directive is used to initialize the data in the $scope.
What ng-init
does is that it will look at the expression and initialize the value of the $scope
variable. So let's say we want to initialize the value of $scope.message2
using ng-init
rather than in our controller, we can do something like the following:
Now if we run he application, we can see that the $scope.message2
was initialized using ng-init
and its value appears on the screen.
However, we should never use ng-init
in this manner because this will lead to the spaghetti code again. We will have a tough time identifying whether the value of a particular scope variable is initialized in the controller or in the view using ng-init
. The recommended use of ng-init
is only to alias the code that is needed in the view only. One example could be that we might have a long filter expression in our view and we want to use this filter expression in multiple places. In this case, we can use ng-init
to alias this expression and use the alias in all the needed places in the view.
ng-bind
The ngBind
directive can be used to display the properties on $scope
on the view. When we use the expression {{ ... }}
, it is also equivalent to ng-bind
. So if we want to use the ngBind
directive to display the message, we need to do the following in the view.
If we run the page, we can still see the message being displayed like before but this time we are explicitly using the ngBind
directive instead of the {{ .. }}
syntax. The important thing to note while using ngBind
is that it is a one way binding, i.e., if we use ng-Bind
to bind a scope
property to an input field, changing the input field will not change the value of the scope
property. If we need this behaviour, we need to use ngModel
instead.
Note: If we want to bind the raw HTML text to the view, there is a directive called bgBindHtml
available that can be used.
ng-model
ngModel
is also very similar to the ngBind
in the sense that it attaches the scope properties with view. But the most important difference and perhaps the benefit is that, ngModel
gives us a two way binding. So if we attach an input field with a scope property using ngModel
, changing the input field value will also change the value of the scope property. Let's see how we can use ngModel
on our view.
Now if we change the value of the textbox
, we can see the value of the scope
property also being changed.
ng-repeat
There are scenarios where we might want to iterate over a collection and display each individual element on the view. ngRepeat
is useful for such scenarios. ngRepeat
can iterate over all the items of the collection and display each item in the specified view format. Let's see how this can be used to iterate over a list of books defined our $scope.books
.
The above code will use ng-repeat
directive to iterate over the list of books ad display each book in a row.
ng-options
The ngRepeat
directive that we saw above can be used to populate the values in dropdown lists. But Angular provides a directive specifically for this purpose. ngOptions
lets us populate the options of dropdown list easily. Let's say we have a list of countries specified in an array on the $scope.countries
variable.
$scope.countries = [
"India",
"Denmark",
"USA",
"Singapore",
"Germany"
];
Now if we want to display this list as options of dropdown list, we can do this in the following manner.
What the above code is doing is that it is binding the select
list with the countries property using the ng-model
directive. Then, we are using ng-options
directive to fill the options of the select list from the countries
collection. If we run this, we can see the values being shown as options of the dropdown list.
ng-click
When using Angular, we don't have to explicitly attach the click events with the DOM elements to handle the click behaviour. Angular provides a directive called ng-click
which lets us easily handle the click events on the DOM elements. What we can do is that we can use ng-click
directive to attach a controller
function with the click
event and whenever a click
occurs, the controller
function will get invoked.
Let's first define a simple function on the scope (in the controller) that will be invoked when a button is clicked.
$scope.fetchBooks = function () {
$scope.books = [
{ ID: 1, BookName: "Test Books 1", AuthorName: "Test Author 1", ISBN: "TEST1" },
{ ID: 2, BookName: "Test Books 2", AuthorName: "Test Author 2", ISBN: "TEST2" },
{ ID: 3, BookName: "Test Books 3", AuthorName: "Test Author 3", ISBN: "TEST3" },
{ ID: 4, BookName: "Test Books 4", AuthorName: "Test Author 4", ISBN: "TEST4" },
{ ID: 5, BookName: "Test Books 5", AuthorName: "Test Author 5", ISBN: "TEST5" }
];
}
Currently, this function is populating the $scope.books
with some hard coded values. Now let's look at the ng-click
directive in the view.
Now when we click on the button, the fetchBooks
function will get called and the $scope.books
will get populated with some predefined values which in turn will be visible on screen.
Note: Similar to ngClick
, there are few other directives available that make the DOM event handling very easy. Few of these directives are: ngDblClick, ngMousedown, ngMouseenter, ngMouseleave, ngMousemove, ngMouseover, ngMouseup, ngKeyPress, ngKeyDown, ngKeyUp, ngBlur, ngFocus.
ng-show & ng-hide
There are two directives, ngShow
and ngHide
which evaluate the expression provided and then selectively show or hide the UI elements. So let's say in our current application, we want to display a message "No books available
" till the user clicks on the button. We can do that by using ng-hide
directive.
What the above code is doing is that it is hiding the message when the count of number of books is more than 0
. So when we start the app, the page will look like:
Now let's say once the button is clicked, the count will be more than 0
, we now need to show the books details in a table. Let's see how we can do this using ng-show
directive.
Now when we click on the button, the table will show up with the list of books.
Note: It is important to note that the ng-show
and ng-hide
only plays with the visibility of the DOM elements. The element will still be rendered on the page just hidden. If we want to prevent the rendering of the element itself, then we should use ngIf
directive instead.
ng-style and ng-class
If we want to apply a CSS style to any of the DOM elements, then we can use ng-style
directive as ng-style="{color: mycolor}"
where my color is a variable defined on scope. ng-style
uses the actual value to apply the CSS style and the value comes from a property defined on scope. There is a similar directive called ng-class
which can be used to apply a CSS class on a DOM element. How ng-class
works is like it evaluates a boolean expression and applies the class if the expression is true
. So if we do something like "ng-class={myclass: books.length > 0}"
, if books.length > 0
evaluates to true
, myclass
will be applied on the DOM element. The actual styles for this class can then be defined in the CSS file.
Point of Interest
In this article, we tried to look at what Angular directives are. We also looked at few of the built in directives in Angular. There are a lot of built in directives available in Angular that we have not talked about here. Also, a lot of community created directives are also available to use. So perhaps before trying to solve any problem the old school way, it is always to good idea to check if there is any built in or third party directive available for use.
The idea of this article was to get the reader acquainted with the way Angular directives work by showing the usage of some common built in directives. In later part of this series, we will also look at how to create a custom directive ourselves. This article has been written from a beginner's perspective and I hope it has been informative.
History
- 6th July, 2015: First version