Categories: angularjs

AngularJS Custom-Directives replace option guide

AngularJS Directive’s replace option can be used to replace the container element itself by directive content. By default, the directive content inserted as the child of the element directive is applied on. But using replace, that container element altogether can be replaced by directive’s actual content HTML.

This post is a part of AngularJS Directives Tutorial Series.

Let’s understand this with help of few examples. Below is the live application from previous example:

Live Example:

If you inspect each item of this demo-example in Developer tools of your browser, you will find something like this:

<div ng-repeat="bla in ctrl.items" class="ng-scope">
 <item-widget item="bla" promo="Christmas-Sale" on-select="ctrl.onItemSelect(selectedItem)" class="ng-isolate-scope">
  <div class="panel panel-default">
   <div class="panel-heading ng-binding">
    Published at:<span ng-bind="item.published | date" class="ng-binding">01/11/2015</span> &nbsp;Promotion: Christmas-Sale<button class="pull-right" ng-click="pickMe({selectedItem:item.name})">Buy me</button>
   </div>
   <div class="panel-body">
    Name:<span ng-bind="item.name" class="ng-binding">Computer</span>
    Condition:<span ng-bind="item.condition" class="ng-binding">New</span>
    Price:<span ng-bind="item.price | currency" class="ng-binding">$500.00</span>
    Brand:<span ng-bind="item.brand" class="ng-binding">Lenovo</span>
   </div>
  </div>
 </item-widget>
</div>

You can see that there is an item-widget element for each item [or a specific ‘div’ if item-widget were used as an attribute of that div].

Whenever AngularJS encounters a directive, it fetches the content of that directive and insert it as a child of the element the directive was applied on. In this case it is the item-widget element itself. This is the default behavior. But sometimes, you may prefer the original element [ the directive is applied on] to be completely replaced by directive content.

AngularJS provides replace key as part of directive definition. It’s default value is false, means the container element will remain there. By setting it to true, you can replace the container element by actual content of directive. All attributes of the container element will then be migrated on the top level element in directive content.

Let’s add replace key in previous example, like shown below:

   .directive('itemWidget', [function() {
    return{
     replace:true,
     ....//other elements as in above example.
   }]);

Live Example using replace option:

Now if you check each item in Developer tools, you will find following content:

<div ng-repeat="bla in ctrl.items" class="ng-scope">
 <div class="panel panel-default ng-isolate-scope" item="bla" promo="Christmas-Sale" on-select="ctrl.onItemSelect(selectedItem)">
  <div class="panel-heading ng-binding">
   Published at:<span ng-bind="item.published | date" class="ng-binding">01/11/2015</span> &nbsp;Promotion: Christmas-Sale<button class="pull-right" ng-click="pickMe({selectedItem:item.name})">Buy me</button>
  </div>
  <div class="panel-body">
    Name:<span ng-bind="item.name" class="ng-binding">Computer</span>
    Condition:<span ng-bind="item.condition" class="ng-binding">New</span>
    Price:<span ng-bind="item.price | currency" class="ng-binding">$500.00</span>
    Brand:<span ng-bind="item.brand" class="ng-binding">Lenovo</span>
  </div>
 </div>
</div>

The <item-widget> element is removed altogether , and all it’s attributes are migrated to top level div of actual content.

Remark: If you see carefully in developer tools, you will find that <div ng-repeat=”bla in ctrl.items” class=”ng-scope”> itself is repeated for each item. This is because for ng-repeat directive, the replace key is false.

Complete Code:

<html>
 <head>  
  <title>Directive Demo</title>  
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
 </head>
 <body class="jumbotron container" ng-app="myApp">
  <div ng-controller="AppController as ctrl">
   <h3>List of Sale Items.</h3>
   <div ng-repeat="bla in ctrl.items">
    <item-widget item="bla" promo="Christmas-Sale" on-select="ctrl.onItemSelect(selectedItem)"></item-widget>
   </div>
  </div>
      
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.js">
  </script>  
  <script>
   angular.module('myApp', [])
   .controller('AppController', [function() {
    var self = this;
    self.items = [
       {name: 'Computer', price: 500, condition:'New',brand : 'Lenovo', published:'01/11/2015'},
       {name: 'Phone', price: 200, condition:'New',brand : 'Samsung', published:'02/11/2015'},
       {name: 'Printer', price: 300, condition:'New',brand : 'Brother', published:'06/11/2015'},
       {name: 'Dishwasher', price: 250, condition:'Second-Hand',brand : 'WhirlPool', published:'01/12/2015'},
       ];
    self.onItemSelect = function(name) {
     console.log('Congrats. You have just bought a', name);
    };       
   }])
   .directive('itemWidget', [function() {
    return{
     restrict: 'E',
     replace:true,
     scope: {
      item: '=',
      promo: '@',
      pickMe : '&onSelect'
     },
        templateUrl: 'saleItem3.html',
     link : function(scope, element, attrs){
         //Add event listener for 'click' event     
      element.on('click', function(event) {
      
        element.html('Thanks for buying this item.');
        element.css({
         color: 'green'
        });
        });
     }
    }
   }]);
  </script>
 </body>
</html>

Shown below is the directive’s content[saleItem3.html].

<div class="panel panel-default">
 <div class="panel-heading">
  Published at:<span ng-bind="item.published | date"></span> &nbsp;Promotion: {{promo}}<button class="pull-right" ng-click="pickMe({selectedItem:item.name})">Buy me</button>
 </div>
 <div class="panel-body">
   Name:<span ng-bind="item.name"></span>
   Condition:<span ng-bind="item.condition"></span>
   Price:<span ng-bind="item.price | currency"></span>
   Brand:<span ng-bind="item.brand"></span>
 </div>
</div>

That’s it for replace option. Let’s move to next configuration option [transclude with ngTransclude] of Directive Definition Object, which can be used to create directive that wraps other elements.

Next Topic : AngularJS Directives transclude option with ngTransclude


Please note that if your AngularJS application is using templateUrl to access HTML partials, you will need an HTTP server on front to serve them. This is due to the fact that Browser’s does not allow serving or requesting files on the file:// protocol.

You can use popular Apache HTTP server to serve files. If you need assistance setting-up server,
this post can help you.

Download Source Code

References

Share
Published by

Recent Posts

Spring Boot + AngularJS + Spring Data + JPA CRUD App Example

In this post we will be developing a full-blown CRUD application using Spring Boot, AngularJS, Spring Data, JPA/Hibernate and MySQL,…

7 years ago

Spring Boot Rest API Example

Spring Boot complements Spring REST support by providing default dependencies/converters out of the box. Writing RESTful services in Spring Boot…

7 years ago

Spring Boot WAR deployment example

Being able to start the application as standalone jar is great, but sometimes it might not be possible to run…

7 years ago

Spring Boot Introduction + hello world example

Spring framework has taken the software development industry by storm. Dependency Injection, rock solid MVC framework, Transaction management, messaging support,…

7 years ago

Secure Spring REST API using OAuth2

Let's secure our Spring REST API using OAuth2 this time, a simple guide showing what is required to secure a…

8 years ago

AngularJS+Spring Security using Basic Authentication

This post shows how an AngularJS application can consume a REST API which is secured with Basic authentication using Spring…

8 years ago