Directives are one of the most powerful concepts in Angular since they let you create custom HTML elements specific to your application. This allows you to develop reusable components, which encapsulate complex DOM structures, style sheets, and even behavior.
Enabling/Disabling DOM Elements Conditionally
Problem
You wish to disable a button depending on a checkbox state.
Solution
Use the ng-disabled directive and bind its condition to the checkbox state:
<body ng-app> <label><input type="checkbox" ng-model="checked"/>Toggle Button</label> <button ng-disabled="checked">Press me</button></body>
You can find the complete example on GitHub.
Discussion
The ng-disabled directive is a direct translation from the disabled HTML attribute, without you needing to worry about browser incompatibilities. It is bound to the checked model using an attribute value as is the checkbox using the ng-model directive. In fact, the checked attribute value is again an Angular expression. You could, for example, invert the logic and use !checked instead.
This is just one example of a directive shipped with Angular. There are many others, for example, ng-hide, ng-checked, or ng-mouseenter. I encourage you to go through the Application Programming Interface (API) Reference and explore all the directives Angular has to offer.
In the next recipes, we will focus on implementing directives.
Changing the DOM in Response to User Actions
Problem
You wish to change the CSS of an HTML element on a mouse click and encapsulate this behavior in a reusable component.
Solution
Implement a directive my-widget that contains an example paragraph of text you want to style:
<body ng-app="MyApp"> <my-widget> <p>Hello World</p> </my-widget></body>
Use a link function in the directive implementation to change the CSS of the paragraph:
var app = angular.module("MyApp", []);app.directive("myWidget", function() { var linkFunction = function(scope, element, attributes) { var paragraph = element.children()[0]; $(paragraph).on("click", function() { $(this).css({ "background-color": "red" }); }); }; return { restrict: "E", link: linkFunction };});
When clicking on the paragraph, the background color changes to red.
You can find the complete example on GitHub.
Discussion
In the HTML document, use the new directive as an HTML element my-widget, which can be found in the JavaScript code as myWidget again. The directive function returns a restriction and a link function.
The restriction means that this directive can only be used as an HTML element and not, for example, an HTML attribute. If you want to use it as an HTML attribute, change the restrict to return A instead. The usage would then have to be adapted to:
<div my-widget> <p>Hello World</p></div>
Whether or not you use the attribute or element mechanism will depend on your use case. Generally speaking, one would use the element mechanism to define a custom reusable component. The attribute mechanism would be used whenever you want to configure some element or enhance it with more behavior. Other available options are using the directive as a class attribute or a comment.
The directive method expects a function that can be used for initialization and injection of dependencies:
app.directive("myWidget", function factory(injectables) { // ...}
The link function is much more interesting since it defines the actual behavior. The scope, the actual HTML element my-widget, and the HTML attributes are passed as params. Note that this has nothing to do with Angular's dependency injection mechanism. Ordering of the parameters is important!
First, we select the paragraph element, which is a child of the my-widget element using Angular's children() function as defined by element. In the second step, we use jQuery to bind to the click event and modify the css property on click. This is of particular interest since we have a mixture of Angular element functions and jQuery here. In fact, under the hood Angular will use jQuery in the children() function if it is defined and will fall back to jqLite (shipped with Angular) otherwise. You can find all supported methods in the API Reference of element.
Following a slightly altered version of the code, using jQuery only:
element.on("click", function() { $(this).css({ "background-color": "red" });});
In this case, element is already a jQuery element, and we can directly use the on function.
Rendering an HTML Snippet in a Directive
Problem
You wish to render an HTML snippet as a reusable component.
Solution
Implement a directive and use the template attribute to define the HTML:
<body ng-app="MyApp"> <my-widget/></body>var app = angular.module("MyApp", []);app.directive("myWidget", function() { return { restrict: "E", template: "<p>Hello World</p>" };});
You can find the complete example on GitHub.
Discussion
This will render the Hello World paragraph as a child node of your my-widget element. If you want to replace the element entirely with the paragraph, you will also have to return the replace attribute:
app.directive("myWidget", function() { return { restrict: "E", replace: true, template: "<p>Hello World</p>" };});
Another option would be to use a file for the HTML snippet. In this case, you will need to use the templateUrl attribute, for example, as follows:
app.directive("myWidget", function() { return { restrict: "E", replace: true, templateUrl: "widget.html" };});
The widget.html should reside in the same directory as the index.html file. This will only work if you use a web server to host the file. The example on GitHub uses angular-seed as a bootstrap again.