Custom Directives in AngularJS is pretty vast topic. I thought of covering it in a single post but as I started writing, count is just increasing as I don’t want to leave any important and relevant aspect. This is third post on custom directive and twelfth post in the series of AngularJS (Click here to see all the post). Till Now, we have discussed that how to create a basic custom directive in first post then in second , we discussed about creating custom directive with isolate scope. The link of those two posts are below –
Tag Archives: Custom Directive
Create Custom Directive using Isolate scope in AngularJS – Part 10
This is the tenth post in the series of AngularJS (Get the list of all previous posts, click here) and this post in the continuation of last post where we have discussed the basics of custom directive (I will recommend to read last post before continuing, Click here to see previous post) but the real value of a custom directive, if it is reusable and can be independently used at many places. If you are using the parent scope directly in your scope then it wont be reusable. One more side effect, if the parent scope gets updated then your custom directive will also be affected even if you don’t want. Not clear? Let’s see an example
var myangularapp = angular.module("myangularapp", []); myangularapp.directive('customdirective', function () { var directive = { restrict : 'E', // restrict this directive to elements template: "<input type='text' placeholder='Enter message...' ng-model='message' />" + " Your Message : {{message}}" }; return directive; });
I have created a directive where user is allowed to enter some message as it and created multiple instances of same directive as
<customdirective> </customdirective> <customdirective> </customdirective> <customdirective> </customdirective>
Now let’s see it running
Here we can see that even we are changing the values in one directive but its getting reflected in all the directives. It is because all are accessing the same scope. Certainly we don’t want this. This problem can be resolved using isolate scope.
What is isolate scope ?
The problems that we discussed above, can be resolved by isolate scope. We can isolate the scope easily that is used in the custom directive. To add isolate scope, we need to add the scope property while defining the directive. It also makes sure that parent scope is not available to the custom directive.
But now the question arises how will the directive interact with the outside world? Because the directive cannot be useful if it works in complete isolation. To handle it, Isolate scope provides us three options to communicate to isolate directive that is also known as Local Scope properties. These are
Let’s discuss each in details
@ or @attr : @ provides us capability to pass a value to custom directive in string format. I have made the string format as bold because I wanted to highlight it, here we cannot pass an object. If you pass an object it will be treated as string format only and accordingly displayed. Also, it works like one way binding it means if the data changes in parent scope, then it reflects in the directive as well as
So here we find that a change takes place in parent scope, it reflects in directive itself but vice versa is not true. If it gets changed inside directive, parent scope does not get affected. Now, How to create the directive?
So here we have created a directive and used it. The details of three points (in pic) are
1- talkname is a property in the isolate scope and it is only accessible in directive itself
2- @talk means that this would be the attribute name in the custom directive that will be used to communicate. We can also write scope : { talkname: ‘@’ }, in this case, talk and talkname both would be same. In this example, I have used different name for better understanding.
3- This is passed from parent scope. As I mentioned in earlier section, that if parent changes then it would reflect in directive as well.
The complete code is as
<script language="javascript" type="text/javascript"> var myangularapp = angular.module("myangularapp", []); myangularapp.controller("mycontroller", function ($scope) { $scope.talk = { name : 'Building modern web apps with ASP.NET 5', duration : '60m'} }); myangularapp.directive('attrcustomdirective', function() { var directive = { restrict : 'E', // restrict this directive to elements scope : { talkname: '@talk' }, template : "<div>{{talkname}}</div> ", }; return directive; }); </script> </head> <body ng-app="myangularapp" ng-controller="mycontroller"> <attrcustomdirective talk="{{talk.name}}" /> </body>
Now let’s move to another type.
= or =attr :
Unlike the @ property which allows us to pass only string value, it allows us to pass the object itself to directive. And the data also gets synced in parent and child scope like two data binding. If the data changes from inside the directive it reflects in parent scope.
Here talk (whole object) is passed in directive and assigned to talkinfo. Now whether the talk or talkinfo gets updated both remains always in sync.
We can see from above that how the directive got created with = and its uses. The details of three points (in pic) are
1- talkinfo here is the object that that got received via talkdetails. You can see, I have accessed the value of talkinfo three times via its properties.
2- talkdetails is attribute name that is used to pass the object via directive. Similar as earlier if we don’t provide the attr as scope : { talkinfo: ‘=’ } then the attribute name will be talkinfo only.
3- talk is the scope object that is assigned to talkdetails.
The complete example will be as
<script language="javascript" type="text/javascript"> var myangularapp = angular.module("myangularapp", []); myangularapp.controller("mycontroller", function ($scope) { $scope.talk = { name : 'Building modern web apps with ASP.NET5', duration : '60m'} }); myangularapp.directive('bindcustomdirective', function() { var directive = { restrict : 'E', // restrict this directive to elements scope : { talkinfo: '=' }, template: "<input type='text' ng-model='talkinfo.name'/>" + "<div>{{talkinfo.name}} : {{talkinfo.duration}}</div> ", }; return directive; }); </script> </head> <body ng-app="myangularapp" ng-controller="mycontroller"> <bindcustomdirective talkdetails="talk"/>{{talk.name}} </body>
Let’s move to last Local scope property.
& or &attr
This is the third and last isolate local scope property. It allows us to wire up external expression to the isolate directive. It could be very useful at certain scenario, where we don’t have details about the expression while defining the directive like we want to call some external function when some event occurs inside the directive based on requirement.
In the above pic, we see that we have function with name method that is passed to directive as via the attribute named expr. Let’s see how do we create the directive and how different property and attributes are co-related.
In the example above, I have used two directives & and @. @ is just used to support this example. Although you must have understand the three points that I have used in the above pic as it is similar to earlier but let me explain it once more in this context. In this example, we are updating an object that gets updated and reflected in the directive as well because of one way binding behavior of @ local scope property
1- method is the property of inner scope here so it is used to access the passed method
2- expr is the attribute name that is used in the directive to pass the expression or defined method. Behavior would be same as earlier local scope property if we just write scope : { method: ‘&’}
3- UpdateData() is the method name that we want to pass in the directive and it is defined as part of parent scope.
4- This value gets updated when we click on Update Data button that calls UpdateData() method which updated the object Talk.
Let’s see the complete example.
<script language="javascript" type="text/javascript"> var myangularapp = angular.module("myangularapp", []); myangularapp.controller("mycontroller", function ($scope) { $scope.talk = { name: 'Building modern web apps with ASP.NET5', duration: '60m' } $scope.UpdateData = function () { $scope.talk = { name: 'Working with AngularJS', duration: '45m' }; }; }); myangularapp.directive('expcustomdirective', function() { var directive = { restrict : 'E', // restrict this directive to elements scope : { method: '&expr', talkname : '@'}, template: "<div>{{talkname}}</div> <input type='button' " + "ng-click='method()' value='Update Data'/> ", }; return directive; }); </script> </head> <body ng-app="myangularapp" ng-controller="mycontroller"> <expcustomdirective expr="UpdateData()" talkname="{{talk.name}}" /> </body>
Hope you must have got good idea about local scope properties in isolate scope and will be able to decide easily that what to use. Do share your feedback and questions.
To navigate to previous and next post of the series, use the links at the bottom of the post.
Happy learning
Brij
Creating custom directive in AngularJS – Part 9
In this post, we’ll discuss how to create custom directive. This is a big topic so I will be discussing in coming couple of posts.As a whole, this is ninth post in series of Learning AngularJS and I hope you are enjoying this series. The link of my earlier posts in the series are below
Learning {{AngularJS}} with Examples–Part 1
Learning {{AngularJS}} with Examples–Part 2
Learning {{AngularJS}} with ASP.NET MVC – Part 3
Learning {{AngularJS}} with ASP.NET MVC – Part 4
Learning {{AngularJS}} with ASP.NET MVC – Part 5
Learning {{AngularJS}} with ASP.NET MVC – Part 6
DataBinding in {{AngularJS}} – Part 7
Data Binding in {{AngularJS}} : Under the hood – Part 8
So let’s come to topic and start from the basics.
What are Directives?
From Angular’s documentation “At a high level, directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS’s HTML compiler ($compile
) to attach a specified behavior to that DOM element or even transform the DOM element and its children.”
So anything that we write in HTML that is handled by Angular are Directives like {{}}, ng-click, ng-repeat etc.
How Directives are handled?
Directives provides us a capability to have more control and flexible way to rendering the html. As we know these are not standard HTML attribute so browser doesn’t understand it. Angular chips in between and reads all the directives and process them to emit the browser readable HTML. So what is the process of handling directives? I already discussed that in my last post, but I am including it here briefly.
So there are two steps involved here: Compiling and Linking.
Type of Directives
There are four types of directives. These are
- Element directives
- Attribute directives
- CSS class directives
- Comment directives
Element Directives – Element directives are like HTML elements as
<myElementDirective></myElementDirective>
Attribute Directive – Attribute directives are which can be added an attribute over an element as
<div myAttrDirective></div>
CSS class directives– These directive can be added as an CSS class
<div class="myAttrDirective: expression;"></div>
Comment Directives– Comment directives is represented as
<!-- directive: myCustomAttrDirective expression -->
How to create a custom directive
One of the key benefits of AngularJS is that apart from its built-in directives, it allows us to write our own custom directives so that we can render the HTML on browsers based on our specific requirement. Angular provides us a simple syntax to create our own custom directive.
var myElementDirective = function () { var myDirective = {}; myDirective.restrict: 'E', //E = element, A = attribute, C = class, M = comment myDirective.scope: { // To provide scope specific for that directive }, myDirective.template: '<mdiv>This is custom directive</div>', myDirective.templateUrl: '/myElementTemplate.html', // use either template or templateUrl to provide the html myDirective.controller: controllerFunction, //custom controller for that directive myDirective.link: function ($scope, element, attrs) { } //DOM manipulation }
The brief description of each property as
restrict | Defines the type of directive. Possible values are E (element), A (Attribute), C (Class), M (Comment) or any combination of these four |
scope | Allows us to provide a specific scope for the element or child scopes |
template | This contains the HTML content that would be replaced in place of directive. It also could contain any other angular directive |
templateUrl | Allows us to provide the URL of the HTML template that contains the actual html content similar to above. Set any one of the two. |
controller | controller function for the directive |
So we got the details about creating a custom directive. But the next question is – How to register it? As we discussed on one my earlier posts (part-2) that Module works as container in AngularJS so the same applies here as well. It means we need to register it with module as directive. We’ll see that coming example.
Let’s create an example
So I am going to create a simple element directive as
var myangularapp = angular.module("myangularapp", []); myangularapp.directive('myelementdirective', function () { var directive = {}; directive.restrict = 'E'; //restrict this directive to elements directive.template = "Hello World using Custom Directive"; return directive; });
and HTML is
<body ng-app="myangularapp"> <myelementdirective></myelementdirective> </body>
No when we run this page, it will appear as
So here we can see the element myelementdirective is replaced with the text. In this example, I have used only few properties that I discussed.
So here we can see that this is an element directive as restrict is set to E (Element). restrict property can be also set as any combinations of E,A,C,M like EA or EAC. If we set it as EA then compiler will find myelementdirective as an element or attribute and replace it accordingly.
We can also use templateUrl instead of template. templateUrl will pick the content of the file and use it as template.
Now, Let’s understand the flow of it working
1- When the application loads, Angular Directive is called which registers the directive as defined.
2- Compiler find out any element with the name myelementdirective appears in the HTML.Here, It finds at one place.
3- Replaces that with the template.
So looks simple. Right!! Let’s see another example
myangularapp.directive('myelementdirective', function () { var directive = {}; directive.restrict = 'E'; //restrict this directive to elements directive.template = "Hello {{name}} !! Welcome to this Angular App"; return directive; });
So here we used an interpolate directive {{}} in the template and used name. So we need to provide the value for this. So let’s add a controller to handle that
myangularapp.controller("mycontroller", function ($scope) { $scope.name = 'Brij'; });
Now when we run the application, Would it work?
So it did work. But in this case, what would be the flow?
As I mentioned earlier that there are steps involved while rendering a page. Compiling and linking. All the data population takes place in linking phase. So during compilation phase it replaced the directive and in linking phase the data got populated from the scope. So earlier steps would be there, just one more steps would be inserted in between 2nd and 3rd.
Using Transclude
This word may look complex but it provides a very useful feature. Till now we have seen that custom directive are just replaced by the template that we provide while creating custom directive but there could be some scenario where we donot want to replace the whole html but only some part of it. Like say I want that the text that is wrapped by the custom directive should not get replaced but only element part. Didn’t get? Let’s see pictorially
Say I created a custom element directive as myCustomDirective and the template provided for it as <div>Some custom template text<div>
In case of transclude
Note – Here I changed the template as <div ng-transclude>Some custom template text</div> to get the transclusion working.
So in the above pic, we can see that the text is not replaced while in case normal directive the whole template replaces the directive.
Now we understood the transclusion so let us see what all we need to do to use it. There are two steps
1- Set the transclude property to true while creating directive.
2- We need to inform Angular that which part contains the transcluded html that’s why for transclusion example, I changed the template a bit (Refer Note above)
Hope you have enjoyed this post. In the next post, we’ll some more interesting feature of custom directives. Stay tuned.
Happy Learning
Brij