| <a href='http://github.com/angular/angular.js/edit/master/docs/content/guide/directive.ngdoc' class='improve-docs btn btn-primary'><i class="glyphicon glyphicon-edit"> </i>Improve this doc</a> |
| |
| |
| <h1 id="creating-custom-directives">Creating Custom Directives</h1> |
| <div class="alert alert-warning"> |
| <strong>Note:</strong> this guide is targeted towards developers who are already familiar with AngularJS basics. |
| If you're just getting started, we recommend the <a href="tutorial/">tutorial</a> first. |
| If you're looking for the <strong>directives API</strong>, we recently moved it to <a href="api/ng/service/$compile"><code>$compile</code></a>. |
| </div> |
| |
| |
| <p>This document explains when you'd want to create your own directives in your AngularJS app, and |
| how to implement them.</p> |
| <h2 id="what-are-directives-">What are Directives?</h2> |
| <p>At a high level, directives are markers on a DOM element (such as an attribute, element |
| name, or CSS class) that tell AngularJS's <strong>HTML compiler</strong> (<a href="api/ng/service/$compile"><code>$compile</code></a>) to |
| attach a specified behavior to that DOM element or even transform the DOM element and its children.</p> |
| <p>Angular comes with a set of these directives built-in, like <code>ngBind</code>, <code>ngModel</code>, and <code>ngView</code>. |
| Much like you create controllers and services, you can create your own directives for Angular to use. |
| When Angular <a href="guide/bootstrap">bootstraps</a> your application, the |
| <a href="guide/compiler">HTML compiler</a> traverses the DOM matching directives against the DOM elements.</p> |
| <div class="alert alert-info"> |
| <strong>What does it mean to "compile" an HTML template?</strong> |
| |
| For AngularJS, "compilation" means attaching event listeners to the HTML to make it interactive. |
| The reason we use the term "compile" is that the recursive process of attaching directives |
| mirrors the process of compiling source code in |
| <a href="http://en.wikipedia.org/wiki/Compiled_languages">compiled programming languages</a>. |
| </div> |
| |
| |
| <h2 id="matching-directives">Matching Directives</h2> |
| <p>Before we can write a directive, we need to know how Angular's <a href="guide/compiler">HTML compiler</a> |
| determines when to use a given directive.</p> |
| <p>In the following example, we say that the <code><input></code> element <strong>matches</strong> the <code>ngModel</code> directive.</p> |
| <pre><code class="lang-html"><input ng-model="foo"></code></pre> |
| <p>The following also <strong>matches</strong> <code>ngModel</code>:</p> |
| <pre><code class="lang-html"><input data-ng:model="foo"></code></pre> |
| <p>Angular <strong>normalizes</strong> an element's tag and attribute name to determine which elements match which |
| directives. We typically refer to directives by their case-sensitive |
| <a href="http://en.wikipedia.org/wiki/CamelCase">camelCase</a> <strong>normalized</strong> name (e.g. <code>ngModel</code>). |
| However, since HTML is case-insensitive, we refer to directives in the DOM by lower-case |
| forms, typically using <a href="http://en.wikipedia.org/wiki/Letter_case#Computers">dash-delimited</a> |
| attributes on DOM elements (e.g. <code>ng-model</code>).</p> |
| <p>The <strong>normalization</strong> process is as follows:</p> |
| <ol> |
| <li>Strip <code>x-</code> and <code>data-</code> from the front of the element/attributes.</li> |
| <li>Convert the <code>:</code>, <code>-</code>, or <code>_</code>-delimited name to <code>camelCase</code>.</li> |
| </ol> |
| <p>Here are some equivalent examples of elements that match <code>ngBind</code>:</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example79@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example79" |
| module="docsBindExample"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsBindExample', []) .controller('Controller', ['$scope', function($scope) { $scope.name = 'Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)'; }]);</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><div ng-controller="Controller"> Hello <input ng-model='name'> <hr/> <span ng-bind="name"></span> <br/> <span ng:bind="name"></span> <br/> <span ng_bind="name"></span> <br/> <span data-ng-bind="name"></span> <br/> <span x-ng-bind="name"></span> <br/> </div></code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="protractorTest.js" |
| language="js" |
| type="js"> |
| <pre><code>it('should show off bindings', function() { expect(element(by.css('div[ng-controller="Controller"] span[ng-bind]')).getText()) .toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)'); });</code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example79/index.html" name="example-example79"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <div class="alert alert-success"> |
| <strong>Best Practice:</strong> Prefer using the dash-delimited format (e.g. <code>ng-bind</code> for <code>ngBind</code>). |
| If you want to use an HTML validating tool, you can instead use the <code>data</code>-prefixed version (e.g. |
| <code>data-ng-bind</code> for <code>ngBind</code>). |
| The other forms shown above are accepted for legacy reasons but we advise you to avoid them. |
| </div> |
| |
| <p><code>$compile</code> can match directives based on element names, attributes, class names, as well as comments.</p> |
| <p>All of the Angular-provided directives match attribute name, tag name, comments, or class name. |
| The following demonstrates the various ways a directive (<code>myDir</code> in this case) can be referenced |
| from within a template:</p> |
| <pre><code class="lang-html"><my-dir></my-dir> |
| <span my-dir="exp"></span> |
| <!-- directive: my-dir exp --> |
| <span class="my-dir: exp;"></span></code></pre> |
| <div class="alert alert-success"> |
| <strong>Best Practice:</strong> Prefer using directives via tag name and attributes over comment and class names. |
| Doing so generally makes it easier to determine what directives a given element matches. |
| </div> |
| |
| <div class="alert alert-success"> |
| <strong>Best Practice:</strong> Comment directives were commonly used in places where the DOM API limits the |
| ability to create directives that spanned multiple elements (e.g. inside <code><table></code> elements). |
| AngularJS 1.2 introduces <a href="api/ng/directive/ngRepeat"><code>ng-repeat-start</code> and <code>ng-repeat-end</code></a> |
| as a better solution to this problem. Developers are encouraged to use this over custom comment |
| directives when possible. |
| </div> |
| |
| |
| |
| <h3 id="text-and-attribute-bindings">Text and attribute bindings</h3> |
| <p>During the compilation process the <a href="api/ng/service/$compile">compiler</a> matches text and attributes |
| using the <a href="api/ng/service/$interpolate">$interpolate</a> service to see if they contain embedded |
| expressions. These expressions are registered as <a href="api/ng/type/$rootScope.Scope#$watch">watches</a> |
| and will update as part of normal <a href="api/ng/type/$rootScope.Scope#$digest">digest</a> cycle. An |
| example of interpolation is shown below:</p> |
| <pre><code class="lang-html"><a ng-href="img/{{username}}.jpg">Hello {{username}}!</a></code></pre> |
| <h3 id="-ngattr-attribute-bindings"><code>ngAttr</code> attribute bindings</h3> |
| <p>Web browsers are sometimes picky about what values they consider valid for attributes.</p> |
| <p>For example, considering this template:</p> |
| <pre><code class="lang-html"><svg> |
| <circle cx="{{cx}}"></circle> |
| </svg></code></pre> |
| <p>We would expect Angular to be able to bind to this, but when we check the console we see |
| something like <code>Error: Invalid value for attribute cx="{{cx}}"</code>. Because of the SVG DOM API's |
| restrictions, you cannot simply write <code>cx="{{cx}}"</code>.</p> |
| <p>With <code>ng-attr-cx</code> you can work around this problem.</p> |
| <p>If an attribute with a binding is prefixed with the <code>ngAttr</code> prefix (denormalized as <code>ng-attr-</code>) |
| then during the binding will be applied to the corresponding unprefixed attribute. This allows |
| you to bind to attributes that would otherwise be eagerly processed by browsers |
| (e.g. an SVG element's <code>circle[cx]</code> attributes).</p> |
| <p>For example, we could fix the example above by instead writing:</p> |
| <pre><code class="lang-html"><svg> |
| <circle ng-attr-cx="{{cx}}"></circle> |
| </svg></code></pre> |
| <h2 id="creating-directives">Creating Directives</h2> |
| <p>First let's talk about the <a href="api/ng/provider/$compileProvider#directive">API for registering directives</a>. Much like |
| controllers, directives are registered on modules. To register a directive, you use the |
| <code>module.directive</code> API. <code>module.directive</code> takes the |
| <a href="guide/directive#creating-custom-directives_matching-directives">normalized</a> directive name |
| followed by a <strong>factory function.</strong> This factory function should return an object with the different |
| options to tell <code>$compile</code> how the directive should behave when matched.</p> |
| <p>The factory function is invoked only once when the |
| <a href="api/ng/service/$compile">compiler</a> matches the directive for the first time. You can perform any |
| initialization work here. The function is invoked using |
| <a href="api/auto/service/$injector#invoke">$injector.invoke</a> which makes it injectable just like a |
| controller.</p> |
| <div class="alert alert-success"> |
| <strong>Best Practice:</strong> Prefer using the definition object over returning a function. |
| </div> |
| |
| |
| <p>We'll go over a few common examples of directives, then dive deep into the different options |
| and compilation process.</p> |
| <div class="alert alert-success"> |
| <strong>Best Practice:</strong> In order to avoid collisions with some future standard, it's best to prefix your own |
| directive names. For instance, if you created a <code><carousel></code> directive, it would be problematic if HTML7 |
| introduced the same element. A two or three letter prefix (e.g. <code>btfCarousel</code>) works well. Similarly, do |
| not prefix your own directives with <code>ng</code> or they might conflict with directives included in a future |
| version of Angular. |
| </div> |
| |
| <p>For the following examples, we'll use the prefix <code>my</code> (e.g. <code>myCustomer</code>).</p> |
| <h3 id="template-expanding-directive">Template-expanding directive</h3> |
| <p>Let's say you have a chunk of your template that represents a customer's information. This template |
| is repeated many times in your code. When you change it in one place, you have to change it in |
| several others. This is a good opportunity to use a directive to simplify your template.</p> |
| <p>Let's create a directive that simply replaces its contents with a static template:</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example80@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example80" |
| module="docsSimpleDirective"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsSimpleDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.customer = { name: 'Naomi', address: '1600 Amphitheatre' }; }]) .directive('myCustomer', function() { return { template: 'Name: {{customer.name}} Address: {{customer.address}}' }; });</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><div ng-controller="Controller"> <div my-customer></div> </div></code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example80/index.html" name="example-example80"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <p>Notice that we have bindings in this directive. After <code>$compile</code> compiles and links |
| <code><div my-customer></div></code>, it will try to match directives on the element's children. This means you |
| can compose directives of other directives. We'll see how to do that in |
| <a href="guide/directive#creating-custom-directives_demo_creating-directives-that-communicate">an example</a> |
| below.</p> |
| <p>In the example above we in-lined the value of the <code>template</code> option, but this will become annoying |
| as the size of your template grows.</p> |
| <div class="alert alert-success"> |
| <strong>Best Practice:</strong> Unless your template is very small, it's typically better to break it apart into |
| its own HTML file and load it with the <code>templateUrl</code> option. |
| </div> |
| |
| <p>If you are familiar with <code>ngInclude</code>, <code>templateUrl</code> works just like it. Here's the same example |
| using <code>templateUrl</code> instead:</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example81@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example81" |
| module="docsTemplateUrlDirective"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsTemplateUrlDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.customer = { name: 'Naomi', address: '1600 Amphitheatre' }; }]) .directive('myCustomer', function() { return { templateUrl: 'my-customer.html' }; });</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><div ng-controller="Controller"> <div my-customer></div> </div></code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="my-customer.html" |
| language="html" |
| type="html"> |
| <pre><code>Name: {{customer.name}} Address: {{customer.address}}</code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example81/index.html" name="example-example81"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <p>Great! But what if we wanted to have our directive match the tag name <code><my-customer></code> instead? |
| If we simply put a <code><my-customer></code> element into the HTML, it doesn't work.</p> |
| <div class="alert alert-waring"> |
| <strong>Note:</strong> When you create a directive, it is restricted to attribute only by default. In order to |
| create directives that are triggered by element or class name, you need to use the <code>restrict</code> option. |
| </div> |
| |
| <p>The <code>restrict</code> option is typically set to:</p> |
| <ul> |
| <li><code>'A'</code> - only matches attribute name</li> |
| <li><code>'E'</code> - only matches element name</li> |
| <li><code>'C'</code> - only matches class name</li> |
| </ul> |
| <p>These restrictions can all be combined as needed:</p> |
| <ul> |
| <li><code>'AEC'</code> - matches either attribute or element or class name</li> |
| </ul> |
| <p>Let's change our directive to use <code>restrict: 'E'</code>:</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example82@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example82" |
| module="docsRestrictDirective"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsRestrictDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.customer = { name: 'Naomi', address: '1600 Amphitheatre' }; }]) .directive('myCustomer', function() { return { restrict: 'E', templateUrl: 'my-customer.html' }; });</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><div ng-controller="Controller"> <my-customer></my-customer> </div></code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="my-customer.html" |
| language="html" |
| type="html"> |
| <pre><code>Name: {{customer.name}} Address: {{customer.address}}</code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example82/index.html" name="example-example82"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <p>For more on the |
| <a href="api/ng/service/$compile#description_comprehensive-directive-api_directive-definition-object"><code>restrict</code></a> |
| property, see the |
| <a href="api/ng/service/$compile#description_comprehensive-directive-api_directive-definition-object">API docs</a>.</p> |
| <div class="alert alert-info"> |
| <strong>When should I use an attribute versus an element?</strong> |
| |
| Use an element when you are creating a component that is in control of the template. The common case |
| for this is when you are creating a Domain-Specific Language for parts of your template. |
| |
| Use an attribute when you are decorating an existing element with new functionality. |
| </div> |
| |
| <p>Using an element for the <code>myCustomer</code> directive is clearly the right choice because you're not |
| decorating an element with some "customer" behavior; you're defining the core behavior of the |
| element as a customer component.</p> |
| <h3 id="isolating-the-scope-of-a-directive">Isolating the Scope of a Directive</h3> |
| <p>Our <code>myCustomer</code> directive above is great, but it has a fatal flaw. We can only use it once within a |
| given scope.</p> |
| <p>In its current implementation, we'd need to create a different controller each time In order to |
| re-use such a directive:</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example83@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example83" |
| module="docsScopeProblemExample"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsScopeProblemExample', []) .controller('NaomiController', ['$scope', function($scope) { $scope.customer = { name: 'Naomi', address: '1600 Amphitheatre' }; }]) .controller('IgorController', ['$scope', function($scope) { $scope.customer = { name: 'Igor', address: '123 Somewhere' }; }]) .directive('myCustomer', function() { return { restrict: 'E', templateUrl: 'my-customer.html' }; });</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><div ng-controller="NaomiController"> <my-customer></my-customer> </div> <hr> <div ng-controller="IgorController"> <my-customer></my-customer> </div></code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="my-customer.html" |
| language="html" |
| type="html"> |
| <pre><code>Name: {{customer.name}} Address: {{customer.address}}</code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example83/index.html" name="example-example83"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <p>This is clearly not a great solution.</p> |
| <p>What we want to be able to do is separate the scope inside a directive from the scope |
| outside, and then map the outer scope to a directive's inner scope. We can do this by creating what |
| we call an <strong>isolate scope</strong>. To do this, we can use a directive's <code>scope</code> option:</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example84@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example84" |
| module="docsIsolateScopeDirective"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsIsolateScopeDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' }; $scope.igor = { name: 'Igor', address: '123 Somewhere' }; }]) .directive('myCustomer', function() { return { restrict: 'E', scope: { customerInfo: '=info' }, templateUrl: 'my-customer-iso.html' }; });</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><div ng-controller="Controller"> <my-customer info="naomi"></my-customer> <hr> <my-customer info="igor"></my-customer> </div></code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="my-customer-iso.html" |
| language="html" |
| type="html"> |
| <pre><code>Name: {{customerInfo.name}} Address: {{customerInfo.address}}</code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example84/index.html" name="example-example84"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <p>Looking at <code>index.html</code>, the first <code><my-customer></code> element binds the <code>info</code> attribute to <code>naomi</code>, |
| which we have exposed on our controller's scope. The second binds <code>info</code> to <code>igor</code>.</p> |
| <p>Let's take a closer look at the scope option:</p> |
| <pre><code class="lang-javascript">//... |
| scope: { |
| customerInfo: '=info' |
| }, |
| //...</code></pre> |
| <p>The <strong>scope option</strong> is an object that contains a property for each isolate scope binding. In this |
| case it has just one property:</p> |
| <ul> |
| <li>Its name (<code>customerInfo</code>) corresponds to the |
| directive's <strong>isolate scope</strong> property <code>customerInfo</code>.</li> |
| <li>Its value (<code>=info</code>) tells <code>$compile</code> to bind to the <code>info</code> attribute.</li> |
| </ul> |
| <div class="alert alert-warning"> |
| <strong>Note:</strong> These <code>=attr</code> attributes in the <code>scope</code> option of directives are normalized just like |
| directive names. To bind to the attribute in <code><div bind-to-this="thing"></code>, you'd specify a binding |
| of <code>=bindToThis</code>. |
| </div> |
| |
| <p>For cases where the attribute name is the same as the value you want to bind to inside the |
| directive's scope, you can use this shorthand syntax:</p> |
| <pre><code class="lang-javascript">... |
| scope: { |
| // same as '=customer' |
| customer: '=' |
| }, |
| ...</code></pre> |
| <p>Besides making it possible to bind different data to the scope inside a directive, using an isolated |
| scope has another effect.</p> |
| <p>We can show this by adding another property, <code>vojta</code>, to our scope and trying to access it from |
| within our directive's template:</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example85@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example85" |
| module="docsIsolationExample"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsIsolationExample', []) .controller('Controller', ['$scope', function($scope) { $scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' }; $scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' }; }]) .directive('myCustomer', function() { return { restrict: 'E', scope: { customerInfo: '=info' }, templateUrl: 'my-customer-plus-vojta.html' }; });</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><div ng-controller="Controller"> <my-customer info="naomi"></my-customer> </div></code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="my-customer-plus-vojta.html" |
| language="html" |
| type="html"> |
| <pre><code>Name: {{customerInfo.name}} Address: {{customerInfo.address}} <hr> Name: {{vojta.name}} Address: {{vojta.address}}</code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example85/index.html" name="example-example85"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <p>Notice that <code>{{vojta.name}}</code> and <code>{{vojta.address}}</code> are empty, meaning they are undefined. |
| Although we defined <code>vojta</code> in the controller, it's not available within the directive.</p> |
| <p>As the name suggests, the <strong>isolate scope</strong> of the directive isolates everything except models that |
| you've explicitly added to the <code>scope: {}</code> hash object. This is helpful when building reusable |
| components because it prevents a component from changing your model state except for the models |
| that you explicitly pass in.</p> |
| <div class="alert alert-warning"> |
| <strong>Note:</strong> Normally, a scope prototypically inherits from its parent. An isolated scope does not. |
| See the <a href="guide/directive#isolating-the-scope-of-a-directive">"Isolating the Scope of a Directive"</a> section for more information about isolate scopes. |
| </div> |
| |
| <div class="alert alert-success"> |
| <strong>Best Practice:</strong> Use the <code>scope</code> option to create isolate scopes when making components that you |
| want to reuse throughout your app. |
| </div> |
| |
| |
| <h3 id="creating-a-directive-that-manipulates-the-dom">Creating a Directive that Manipulates the DOM</h3> |
| <p>In this example we will build a directive that displays the current time. |
| Once a second, it updates the DOM to reflect the current time.</p> |
| <p>Directives that want to modify the DOM typically use the <code>link</code> option. |
| <code>link</code> takes a function with the following signature, <code>function link(scope, element, attrs) { ... }</code> |
| where:</p> |
| <ul> |
| <li><code>scope</code> is an Angular scope object.</li> |
| <li><code>element</code> is the jqLite-wrapped element that this directive matches.</li> |
| <li><code>attrs</code> is a hash object with key-value pairs of normalized attribute names and their |
| corresponding attribute values.</li> |
| </ul> |
| <p>In our <code>link</code> function, we want to update the displayed time once a second, or whenever a user |
| changes the time formatting string that our directive binds to. We will use the <code>$interval</code> service |
| to call a handler on a regular basis. This is easier than using <code>$timeout</code> but also works better with |
| end 2 end testing, where we want to ensure that all $timeouts have completed before completing the test. |
| We also want to remove the <code>$interval</code> if the directive is deleted so we don't introduce a memory leak.</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example86@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example86" |
| module="docsTimeDirective"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsTimeDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.format = 'M/d/yy h:mm:ss a'; }]) .directive('myCurrentTime', ['$interval', 'dateFilter', function($interval, dateFilter) { function link(scope, element, attrs) { var format, timeoutId; function updateTime() { element.text(dateFilter(new Date(), format)); } scope.$watch(attrs.myCurrentTime, function(value) { format = value; updateTime(); }); element.on('$destroy', function() { $interval.cancel(timeoutId); }); // start the UI update process; save the timeoutId for canceling timeoutId = $interval(function() { updateTime(); // update DOM }, 1000); } return { link: link }; }]);</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><div ng-controller="Controller"> Date format: <input ng-model="format"> <hr/> Current time is: <span my-current-time="format"></span> </div></code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example86/index.html" name="example-example86"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <p>There are a couple of things to note here. |
| Just like the <code>module.controller</code> API, the function argument in <code>module.directive</code> is dependency |
| injected. Because of this, we can use <code>$interval</code> and <code>dateFilter</code> inside our directive's <code>link</code> |
| function.</p> |
| <p>We register an event <code>element.on('$destroy', ...)</code>. What fires this <code>$destroy</code> event?</p> |
| <p>There are a few special events that AngularJS emits. When a DOM node that has been compiled |
| with Angular's compiler is destroyed, it emits a <code>$destroy</code> event. Similarly, when an AngularJS |
| scope is destroyed, it broadcasts a <code>$destroy</code> event to listening scopes.</p> |
| <p>By listening to this event, you can remove event listeners that might cause memory leaks. |
| Listeners registered to scopes and elements are automatically cleaned up when they are destroyed, |
| but if you registered a listener on a service, or registered a listener on a DOM node that isn't |
| being deleted, you'll have to clean it up yourself or you risk introducing a memory leak.</p> |
| <div class="alert alert-success"> |
| <strong>Best Practice:</strong> Directives should clean up after themselves. You can use |
| <code>element.on('$destroy', ...)</code> or <code>scope.$on('$destroy', ...)</code> to run a clean-up function when the |
| directive is removed. |
| </div> |
| |
| |
| <h3 id="creating-a-directive-that-wraps-other-elements">Creating a Directive that Wraps Other Elements</h3> |
| <p>We've seen that you can pass in models to a directive using the isolate scope, but sometimes |
| it's desirable to be able to pass in an entire template rather than a string or an object. |
| Let's say that we want to create a "dialog box" component. The dialog box should be able to |
| wrap any arbitrary content.</p> |
| <p>To do this, we need to use the <code>transclude</code> option.</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example87@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example87" |
| module="docsTransclusionDirective"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsTransclusionDirective', []) .controller('Controller', ['$scope', function($scope) { $scope.name = 'Tobias'; }]) .directive('myDialog', function() { return { restrict: 'E', transclude: true, templateUrl: 'my-dialog.html' }; });</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><div ng-controller="Controller"> <my-dialog>Check out the contents, {{name}}!</my-dialog> </div></code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="my-dialog.html" |
| language="html" |
| type="html"> |
| <pre><code><div class="alert" ng-transclude> </div></code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example87/index.html" name="example-example87"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <p>What does this <code>transclude</code> option do, exactly? <code>transclude</code> makes the contents of a directive with |
| this option have access to the scope <strong>outside</strong> of the directive rather than inside.</p> |
| <p>To illustrate this, see the example below. Notice that we've added a <code>link</code> function in <code>script.js</code> |
| that redefines <code>name</code> as <code>Jeff</code>. What do you think the <code>{{name}}</code> binding will resolve to now?</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example88@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example88" |
| module="docsTransclusionExample"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsTransclusionExample', []) .controller('Controller', ['$scope', function($scope) { $scope.name = 'Tobias'; }]) .directive('myDialog', function() { return { restrict: 'E', transclude: true, scope: {}, templateUrl: 'my-dialog.html', link: function (scope, element) { scope.name = 'Jeff'; } }; });</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><div ng-controller="Controller"> <my-dialog>Check out the contents, {{name}}!</my-dialog> </div></code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="my-dialog.html" |
| language="html" |
| type="html"> |
| <pre><code><div class="alert" ng-transclude> </div></code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example88/index.html" name="example-example88"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <p>Ordinarily, we would expect that <code>{{name}}</code> would be <code>Jeff</code>. However, we see in this example that |
| the <code>{{name}}</code> binding is still <code>Tobias</code>.</p> |
| <p>The <code>transclude</code> option changes the way scopes are nested. It makes it so that the <strong>contents</strong> of a |
| transcluded directive have whatever scope is outside the directive, rather than whatever scope is on |
| the inside. In doing so, it gives the contents access to the outside scope.</p> |
| <p>Note that if the directive did not create its own scope, then <code>scope</code> in <code>scope.name = 'Jeff';</code> would |
| reference the outside scope and we would see <code>Jeff</code> in the output.</p> |
| <p>This behavior makes sense for a directive that wraps some content, because otherwise you'd have to |
| pass in each model you wanted to use separately. If you have to pass in each model that you want to |
| use, then you can't really have arbitrary contents, can you?</p> |
| <div class="alert alert-success"> |
| <strong>Best Practice:</strong> only use <code>transclude: true</code> when you want to create a directive that wraps |
| arbitrary content. |
| </div> |
| |
| <p>Next, we want to add buttons to this dialog box, and allow someone using the directive to bind their |
| own behavior to it.</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example89@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example89" |
| module="docsIsoFnBindExample"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsIsoFnBindExample', []) .controller('Controller', ['$scope', '$timeout', function($scope, $timeout) { $scope.name = 'Tobias'; $scope.hideDialog = function () { $scope.dialogIsHidden = true; $timeout(function () { $scope.dialogIsHidden = false; }, 2000); }; }]) .directive('myDialog', function() { return { restrict: 'E', transclude: true, scope: { 'close': '&onClose' }, templateUrl: 'my-dialog-close.html' }; });</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><div ng-controller="Controller"> <my-dialog ng-hide="dialogIsHidden" on-close="hideDialog()"> Check out the contents, {{name}}! </my-dialog> </div></code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="my-dialog-close.html" |
| language="html" |
| type="html"> |
| <pre><code><div class="alert"> <a href class="close" ng-click="close()">×</a> <div ng-transclude></div> </div></code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example89/index.html" name="example-example89"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <p>We want to run the function we pass by invoking it from the directive's scope, but have it run |
| in the context of the scope where it's registered.</p> |
| <p>We saw earlier how to use <code>=attr</code> in the <code>scope</code> option, but in the above example, we're using |
| <code>&attr</code> instead. The <code>&</code> binding allows a directive to trigger evaluation of an expression in |
| the context of the original scope, at a specific time. Any legal expression is allowed, including |
| an expression which contains a function call. Because of this, <code>&</code> bindings are ideal for binding |
| callback functions to directive behaviors.</p> |
| <p>When the user clicks the <code>x</code> in the dialog, the directive's <code>close</code> function is called, thanks to |
| <code>ng-click.</code> This call to <code>close</code> on the isolated scope actually evaluates the expression |
| <code>hideDialog()</code> in the context of the original scope, thus running <code>Controller</code>'s <code>hideDialog</code> |
| function.</p> |
| <div class="alert alert-success"> |
| <strong>Best Practice:</strong> use <code>&attr</code> in the <code>scope</code> option when you want your directive |
| to expose an API for binding to behaviors. |
| </div> |
| |
| |
| <h3 id="creating-a-directive-that-adds-event-listeners">Creating a Directive that Adds Event Listeners</h3> |
| <p>Previously, we used the <code>link</code> function to create a directive that manipulated its |
| DOM elements. Building upon that example, let's make a directive that reacts to events on |
| its elements.</p> |
| <p>For instance, what if we wanted to create a directive that lets a user drag an |
| element?</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example90@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example90" |
| module="dragModule"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('dragModule', []) .directive('myDraggable', ['$document', function($document) { return function(scope, element, attr) { var startX = 0, startY = 0, x = 0, y = 0; element.css({ position: 'relative', border: '1px solid red', backgroundColor: 'lightgrey', cursor: 'pointer' }); element.on('mousedown', function(event) { // Prevent default dragging of selected content event.preventDefault(); startX = event.pageX - x; startY = event.pageY - y; $document.on('mousemove', mousemove); $document.on('mouseup', mouseup); }); function mousemove(event) { y = event.pageY - startY; x = event.pageX - startX; element.css({ top: y + 'px', left: x + 'px' }); } function mouseup() { $document.unbind('mousemove', mousemove); $document.unbind('mouseup', mouseup); } }; }]);</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><span my-draggable>Drag ME</span></code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example90/index.html" name="example-example90"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <h3 id="creating-directives-that-communicate">Creating Directives that Communicate</h3> |
| <p>You can compose any directives by using them within templates.</p> |
| <p>Sometimes, you want a component that's built from a combination of directives.</p> |
| <p>Imagine you want to have a container with tabs in which the contents of the container correspond |
| to which tab is active.</p> |
| <p> |
| |
| <div> |
| <a ng-href="http://plnkr.co/edit/ngdoc:example-example91@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank"> |
| <i class="glyphicon glyphicon-edit"> </i> |
| Edit in Plunker</a> |
| <div class="runnable-example" |
| path="examples/example-example91" |
| module="docsTabsExample"> |
| |
| |
| <div class="runnable-example-file" |
| name="script.js" |
| language="js" |
| type="js"> |
| <pre><code>angular.module('docsTabsExample', []) .directive('myTabs', function() { return { restrict: 'E', transclude: true, scope: {}, controller: function($scope) { var panes = $scope.panes = []; $scope.select = function(pane) { angular.forEach(panes, function(pane) { pane.selected = false; }); pane.selected = true; }; this.addPane = function(pane) { if (panes.length === 0) { $scope.select(pane); } panes.push(pane); }; }, templateUrl: 'my-tabs.html' }; }) .directive('myPane', function() { return { require: '^myTabs', restrict: 'E', transclude: true, scope: { title: '@' }, link: function(scope, element, attrs, tabsCtrl) { tabsCtrl.addPane(scope); }, templateUrl: 'my-pane.html' }; });</code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="index.html" |
| language="html" |
| type="html"> |
| <pre><code><my-tabs> <my-pane title="Hello"> <h4>Hello</h4> <p>Lorem ipsum dolor sit amet</p> </my-pane> <my-pane title="World"> <h4>World</h4> <em>Mauris elementum elementum enim at suscipit.</em> <p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p> </my-pane> </my-tabs></code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="my-tabs.html" |
| language="html" |
| type="html"> |
| <pre><code><div class="tabbable"> <ul class="nav nav-tabs"> <li ng-repeat="pane in panes" ng-class="{active:pane.selected}"> <a href="" ng-click="select(pane)">{{pane.title}}</a> </li> </ul> <div class="tab-content" ng-transclude></div> </div></code></pre> |
| </div> |
| |
| <div class="runnable-example-file" |
| name="my-pane.html" |
| language="html" |
| type="html"> |
| <pre><code><div class="tab-pane" ng-show="selected" ng-transclude> </div></code></pre> |
| </div> |
| |
| |
| <iframe class="runnable-example-frame" src="examples/example-example91/index.html" name="example-example91"></iframe> |
| </div> |
| </div> |
| |
| </p> |
| <p>The <code>myPane</code> directive has a <code>require</code> option with value <code>^myTabs</code>. When a directive uses this |
| option, <code>$compile</code> will throw an error unless the specified controller is found. The <code>^</code> prefix |
| means that this directive searches for the controller on its parents (without the <code>^</code> prefix, the |
| directive would look for the controller on just its own element).</p> |
| <p>So where does this <code>myTabs</code> controller come from? Directives can specify controllers using |
| the unsurprisingly named <code>controller</code> option. As you can see, the <code>myTabs</code> directive uses this |
| option. Just like <code>ngController</code>, this option attaches a controller to the template of the directive.</p> |
| <p>Looking back at <code>myPane</code>'s definition, notice the last argument in its <code>link</code> function: <code>tabsCtrl</code>. |
| When a directive requires a controller, it receives that controller as the fourth argument of its |
| <code>link</code> function. Taking advantage of this, <code>myPane</code> can call the <code>addPane</code> function of <code>myTabs</code>.</p> |
| <p>Savvy readers may be wondering what the difference is between <code>link</code> and <code>controller</code>. |
| The basic difference is that <code>controller</code> can expose an API, and <code>link</code> functions can interact with |
| controllers using <code>require</code>.</p> |
| <div class="alert alert-success"> |
| <strong>Best Practice:</strong> use <code>controller</code> when you want to expose an API to other directives. |
| Otherwise use <code>link</code>. |
| </div> |
| |
| <h3 id="summary">Summary</h3> |
| <p>Here we've seen the main use cases for directives. Each of these samples acts as a good starting |
| point for creating your own directives.</p> |
| <p>You might also be interested in an in-depth explanation of the compilation process that's |
| available in the <a href="guide/compiler">compiler guide</a>.</p> |
| <p>The <a href="api/ng/service/$compile"><code>$compile</code> API</a> page has a comprehensive list of directive options for |
| reference.</p> |
| |
| |