Book Image

AngularJS Directives Cookbook

Book Image

AngularJS Directives Cookbook

Overview of this book

AngularJS directives are at the center of what makes it such an exciting – and important - web development framework. With directives, you can take greater control over HTML elements on your web pages – they ‘direct’ Angular’s HTML compiler to behave in the way you want it to. It makes building modern web applications a much more expressive experience, and allows you to focus more closely on improving the way that user interaction impacts the DOM and the way your app manages data. If you’re already using Angular, you probably recognize the power of directives to transform the way you understand and build your projects – but customizing and creating your own directives to harness AngularJS to its full potential can be more challenging. This cookbook shows you how to do just that – it’s a valuable resource that demonstrates how to use directives at every stage in the workflow. Packed with an extensive range of solutions and tips that AngularJS developers shouldn’t do without, you’ll find out how to make the most of directives. You’ll find recipes demonstrating how to build a number of different user interface components with directives, so you can take complete control over how users interact with your application. You’ll also learn how directives can simplify the way you work by creating reusable directives – by customizing them with Yeoman you can be confident that you’re application has the robust architecture that forms the bedrock of the best user experiences. You’ll also find recipes that will help you learn how to unit test directives, so you can be confident in the reliability and performance of your application. Whether you’re looking for guidance to dive deeper into AngularJS directives, or you want a reliable resource, relevant to today’s web development challenges, AngularJS Directives Cookbook delivers everything you need in an easily accessible way.
Table of Contents (16 chapters)
AngularJS Directives Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Dealing with tabs without Bootstrap UI directives


Bootstrap user interface is very popular and is used by many web developers. The AngularJS community has their own in-built version on top of the Bootstrap JavaScript library, the AngularJS UI directives. However, using it is not always our first option; often, we need a simple solution.

In this recipe, we will see how to build component tabs without Angular UI.

Later in the book, we will see in depth how to use and customize Bootstrap UI directives. Now, we will focus on a simple directive tabs.

In a very basic way, we don't need to use a custom directive to build the tabs. So, let's see two ways to build a simple tab.

Getting ready

For the first example, we need the following code:

<!DOCTYPE html>
<html ng-app="simpleTab">
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.4/angular.js"></script>
  <title>Simple tab</title>
  <style>
    .tabs-nav {
      padding: 20px 0 0;
      list-style: none;
    }
    .tabs-nav li {
      display: inline;
      margin-right: 20px;
    }
    .tabs-nav a {
      display:inline-block;
      cursor: pointer;
    }
    .tabs-nav .active {
      color: red;
    }
    .tab-content {
      border: 1px solid #ddd;
      padding: 20px;
    }
  </style>
</head>

<div class="tabs-holder" ng-app="simpleTab" ng-init="tab=1">
  <ul class="tabs-nav">
    <li><a ng-click="tab=1" ng-class="{'active' : tab==1}">First tab</a></li>
    <li><a ng-click="tab=2" ng-class="{'active' : tab==2}">Second tab</a></li>
    <li><a ng-click="tab=3" ng-class="{'active' : tab==3}">Third tab</a></li>
  </ul>

  <div class="tabs-container">
    <div class="tab-content" ng-show="tab == 1">
      <h1>First Tab</h1>
      <p>Simple tab 1</p>
    </div>
    <div class="tab-content" ng-show="tab == 2">
      <h1>Second tab</h1>
      <p>Simple tab 2</p>
    </div>

    <div class="tab-content" ng-show="tab == 3">
      <h1>Third Tab</h1>
      <p>Simple tab 3</p>
    </div>
  </div>
</div>
</body>
</html>

For the second example, we need the following code. This time, we're using a controller and an external template. Place the following HTML code in a blank HTML file:

<!DOCTYPE html>
<html ng-app="simpleTabController">
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.4/angular.js"></script>
  <title>Simple tab with Controller</title>
  <style>
    .tabs-nav {
      padding: 20px 0 0;
      list-style: none;
    }
    .tabs-nav li {
      display: inline;
      margin-right: 20px;
    }
    .tabs-nav a {
      display:inline-block;
      cursor: pointer;
    }
    .tabs-nav .active {
      color: red;
    }
    .tab-content {
      border: 1px solid #ddd;
      padding: 20px;
    }
  </style>
</head>
<body>
<div class="tabs-holder" ng-app="simpleTabController">
<div id="tabs" ng-controller="TabsCtrl">
  <ul class="tabs-nav">
    <li ng-repeat="tab in tabs"
    ng-class="{active:isActiveTab(tab.url)}"
    ng-click="onClickTab(tab)">{{tab.title}}</li>
  </ul>
  <div id="tab-content">
    <div ng-include="currentTab"></div>
  </div>
  <!--Script templates-->
  <script type="text/ng-template" id="first.html">
    <div class="tab-content" id="1">
      <h1>First Tab</h1>
      <p>Simple tab 1</p>
    </div>
  </script>

  <script type="text/ng-template" id="second.html">
    <div class="tab-content" id="2">
      <h1>Second Tab</h1>
      <p>Simple tab 2</p>
    </div>
  </script>

  <script type="text/ng-template" id="third.html">
    <div class="tab-content" id="3">
      <h1>Third Tab</h1>
      <p>Simple tab 3</p>
    </div>
  </script>
</div>
</div>
</body>
</html>

How to do it…

With the HTML already set up for both examples, let's dive into the controller's code for the second one. Add the following code to a separate file:

angular.module('simpleTabController', [])

.controller('TabsCtrl', ['$scope', function ($scope) {
  $scope.tabs = [{
    title: 'First tab',
    url: 'first.html'
  }, {
    title: 'Second tab',
    url: 'second.html'
  }, {
    title: 'Third tab',
    url: 'third.html'
}];

  $scope.currentTab = 'first.html';

  $scope.onClickTab = function (tab) {
    $scope.currentTab = tab.url;
  }

  $scope.isActiveTab = function(tabUrl) {
    return tabUrl == $scope.currentTab;
  }
}]);

The result of the tabs example is very similar to the following screenshot:

Simple tab layout example

Note that we keep the layout as simple as possible just for the example code.

For the second example, we keep the same stylesheet and layout. In both the examples, we include the CSS inside the head element on the HTML page; you must avoid this on production applications.

How it works…

The first example is pretty intuitive, and we only use the AngularJS built-in directives, such as ng-class and ng-show, to simulate the tab functionality.

<ul class="tabs-nav">
  <li><a ng-click="tab=1" ng-class="{'active' : tab==1}">First tab</a></li>
  <li><a ng-click="tab=2" ng-class="{'active' : tab==2}">Second tab</a></li>
  <li><a ng-click="tab=3" ng-class="{'active' : tab==3}">Third tab</a></li>
</ul>

Internally, the framework recognizes the reverse state of ng-show and hides all the content of tabs 1 and 2. When we click on one of the other tabs, the state changes to show what has been clicked on and hides the others.

This is a simple example, but it is not very flexible.

In the second example, we added a controller to deal with the tabs logic, creating a $scope to hold the tab title and their respective template:

$scope.tabs = [{
  title: 'First tab',
  url: 'first.html'
}, {
  title: 'Second tab',
  url: 'second.html'
}, {
  title: 'Third tab',
  url: 'third.html'
}];

We could easily introduce other elements in this array, such as description, date, and other elements, since we have loaded them from the controller. Although, it is possible to load the tabs content dynamically within this own array. We can also load the templates in external files, as we saw in the beginning of this chapter.

For this, transfer the contents of the script tags (highlighted here) to external files, keeping the names as first.html, second.html, and third.html:

<script type="text/ng-template" id="first.html">
  <div class="tab-content" id="1">
    <h1>First Tab</h1>
    <p>Simple tab 1</p>
  </div>
</script>

Now just remove the script tags from the original HTML file:

<script type="text/ng-template" id="second.html">
  <div class="tab-content" id="2">
    <h1>Second Tab</h1>
    <p>Simple tab 2</p>
  </div>
</script>

<script type="text/ng-template" id="third.html">
  <div class="tab-content" id="3">
    <h1>Third Tab</h1>
    <p>Simple tab 3</p>
  </div>
</script>

Now we can have tabs with external templates.

These were simple examples for creation of tabs without using custom directives, and instead using built-in AngularJS directives. We highlighted the DOM manipulation's simplicity by using controllers rather than customized directives.

There's more…

In addition to the previous examples, we can easily create a directive to use tabs. So, we address all the possibilities in the creation of this interactive component.

Let's see a directive example:

<!DOCTYPE html>
<html >
<head>
  <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.4/angular.js"></script>
  <title>Simple tab with Directive</title>
  <style>
    .tabs-nav {
      padding: 20px 0 0;
      list-style: none;
    }
    .tabs-nav li {
      display: inline;
      margin-right: 20px;
    }
    .tabs-nav a {
      display:inline-block;
      cursor: pointer;
    }
    .tabs-nav .active {
      color: red;
    }
    .tab-content {
      border: 1px solid #ddd;
      padding: 20px;
    }
  </style>
</head>
<body>
  <div ng-app='simpleTabDirective'>
    <ng-tabs>
      <content-tab dat-heading='First tab' dat-tab-active>
        <h1>First Tab</h1>
        <p>Simple tab 1</p>
      </content-tab>
      <content-tab dat-heading='Second tab'>
        <h1>Second Tab</h1>
        <p>Simple tab 2</p>
      </content-tab>
      <content-tab dat-heading='Third tab'>
        <h1>Third Tab</h1>
        <p>Simple tab 3</p>
      </content-tab>
    </ng-tabs>  
  </div>
</body>
</html>

Now, the controller turns into a directive:

var app = angular.module("simpleTabDirective", [])

app.directive('ngTabs', function() {
  return {
    scope: true,
    replace: true,
    restrict: 'E',
    transclude: true,
    template: ' \
<div class="tab-content"> \
  <ul class="tabs-nav"> \
    <li ng-repeat="tab in tabs" \
        ng-class="{ active: currentTab == $index }"> \
      <a ng-click="selectTab($index)"> \
        {{tab}} \
      </a> \
    </li> \
  </ul> \
  <div ng-transclude></div> \
</div>',
    controller: function($scope) {
      $scope.currentTab = 0;
      
      $scope.tabs = [];
      
      $scope.selectTab = function(index) {
        $scope.currentTab = index;
      };
      
      return $scope;
    }
  }
})

app.directive('contentTab', function() {
  return {
    require: '^ngTabs',
    restrict: 'E',
    transclude: true,
    replace: true,
    scope: true,
    template: '<div class="tab-content" ng-show="showTab()" ng-transclude></div>',
    link: function(scope, element, attrs, ngTabs) {
      var tabId = ngTabs.tabs.length;
      
      scope.showTab = function() {
        return tabId == ngTabs.currentTab;
      };
      
      ngTabs.tabs.push(attrs.datHeading);
    }
  }
});

Note that we use the property require to set the dependence of ngTabs. In this way, our tab consists of two directives, one to create the list where we will have the title of the tabs and the second to create the contents of each tab itself. The code is as follows:

<ng-tabs>
  <content-tab dat-heading='First tab'>
  </content-tab>
</ng-tabs>

We can also observe that we have used all the features seen earlier in this chapter, such as ng-click, ng-repeat, and ng-transclude, among others.

See also

  • A great resource that helps us in search of directives, and other related stuff, for the development of applications with AngularJS is the website Angular Modules (http://ngmodules.org/tags/directive)