1.Table Of Contents

  1. TOC
  2. Introduction
  3. Building Our Basic AngularJS Directive
  4. Adding Dynamic Content Bindings
  5. Dynamically Loading Data Using $http Service
  6. Demo
  7. Conclusion

2.Introduction

This is the third tutorial of the AngularJS directives series using Twitter’s Bootstrap CSS Framework and its JavaScript components. In this part we are going to create a custom tabs directive. We are going to build upon the previous tutorial of the series – AngularJs Bootstrap Components – Part 2 – Building Collapse(Accordion) Directive.

3.Building Our Basic AngularJS Directive

As in the previous tutorials I’ll begin with creating a simple directive that will display static Bootstrap Tabs component and move on to populating the Tabs content with AngularJS model, but this time will also use the $http AngularJS service to asynchronously load the contents of the tabs.

Basic HTML

The initial HTML code for this tutorial will look like this:

<div ng-app="customDirectives">
    <div custom-tabs tabs-base-id="content-tab"></div>
</div>

This time we have two custom attributes:
custom-tabs -will be used by AngularJS to match the element with the directive in the parsing process.
tabs-base-id – will be used to set custom ID base for the tabs. This way you can have as many instances as you like(as long as they have separate scopes).

Basic JavaScript Code

The changes from the previous tutorial in the base directive are mainly in the template:

customDirectives = angular.module('customDirectives', []);
customDirectives.directive('customTabs', function () {
    return {
        restrict: 'A',
        template: '\
            <ul class="nav nav-tabs">\
              <li class="active"><a href="#{{contentBaseId}}-1" data-toggle="tab">Tab 1</a></li>\
              <li><a href="#{{contentBaseId}}-2" data-toggle="tab">Tab 2</a></li>\
              <li><a href="#{{contentBaseId}}-3" data-toggle="tab">Tab 3</a></li>\
              <li><a href="#{{contentBaseId}}-4" data-toggle="tab">Tab 4</a></li>\
            </ul>\
            <div class="tab-content">\
              <div class="tab-pane active" id="{{contentBaseId}}-1">Tab 1 sample content</div>\
              <div class="tab-pane" id="{{contentBaseId}}-2">Tab 2 sample content</div>\
              <div class="tab-pane" id="{{contentBaseId}}-3">Tab 3 sample content</div>\
              <div class="tab-pane" id="{{contentBaseId}}-4">Tab 4 sample content</div>\
            </div>',
        link: function(scope, el, attrs){
            scope.contentBaseId = attrs.tabsBaseId;
        }
    };
});

angular.module('CustomComponents', ['customDirectives']);

Nothing special here.

The Result:

4.Adding Dynamic Content Bindings

Again we’ll need a base controller to keep the model that will hold the contents of the tabs and later use to make the AJAX request.

Adding Base Controller

As in the previous part we are going to use the ng-model directive to pass the data to the directive’s isolated scope and almost the same code for the controller, but this time we’ll have an active property that we’ll use to control which tab will be shown.

<div ng-app="customDirectives">
    <div ng-controller="CustomDirectivesController">
        <div custom-tabs tabs-base-id="content-tab" ng-model="tabsData"></div>
    </div>
</div>
function CustomDirectivesController($scope)
{    
    $scope.tabsData = [
        {
            title: "Tab Title 1",
            content: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. 1",
            active: false
        },
        {
            title: "Tab Title 2",
            content: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. 2",
            active: true
        },
        {
            title: "Tab Title 2",
            content: "Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. 3",
            active: false
        }
    ];
}

Extending The Directive

This time instead of ng-repeat-start and ng-repeat-end we’ll use the default ng-repeat directive because we have two element groups that need to loop the same data object, but are in different parents (the titles and the content).

customDirectives.directive('customTabs', function () {
    return {
        restrict: 'A',
        require: '?ngModel',
        scope:{
            ngModel: '='
        },
        template: '\
            <ul class="nav nav-tabs">\
                <li ng-class="{active: item.active}" ng-repeat="item in ngModel"><a href="#{{contentBaseId}}-{{$index}}" data-toggle="tab">{{item.title}}</a></li>\
            </ul>\
            <div class="tab-content">\
              <div class="tab-pane" ng-class="{active: item.active}" id="{{contentBaseId}}-{{$index}}" ng-repeat="item in ngModel">{{item.content}}</div>\
            </div>',
        link: function(scope, el, attrs){
            scope.contentBaseId = attrs.tabsBaseId;
        }
    };
});
Custom Method For Toggling Collapse Panels

Again we’ll use the ng-click to manually trigger the show method of the tabs and also change the state in the model for later use.

scope.toggleActive = function(ind){
    angular.forEach(scope.ngModel, function(value, key){
        if (key == ind)
        {
            scope.ngModel[key].active = !scope.ngModel[key].active;
            $("#" + scope.panelBaseId + "-" + ind).tab('show');
        }
        else
            scope.ngModel[key].active = false;
    });
}

The Result

Here is what we have so far:

5.Dynamically Loading Data Using $http Service

In this example we’ll load the data using the $http AngularJS service, which is based on the deffered/promise API provided by the $q service.

For the demo to work in jsFiddle I’ll use the jsonp shortcut method for the “JSONP” request of the $http service, so I can fetch the data from my site. I’ll also add two buttons which we’ll use to make jsonp requests to two different files, so the HTML will look like this:

<div ng-app="customDirectives">
    <div ng-controller="CustomDirectivesController">
        <button ng-click="loadTabs('')">Toggle Tabs 1</button>
        <button ng-click="loadTabs('1')">Toggle Tabs 2</button>
        <div custom-tabs tabs-base-id="content-tab" ng-model="tabsData"></div>
    </div>
</div>

For the demo I named my files tabs.js and tabs1.js, but you should probably use a better naming convention.

Now, because I’ll be using jsonp I need to define the getTabs callback method, that will be wrapping the response from the requests, in my controller and also I’ll add the loadTabs method which we’ll use to change the source for the tabs.

function CustomDirectivesController($scope, $http)
{
    getTabs = function(data)
    {
        $scope.tabsData = data.tabs;
    };
    
    $scope.loadTabs = function(num)
    {
        $http.jsonp("http://subliminalsources.com/wp-content/uploads/2014/02/tabs" + num + ".js");
    }
    
    $scope.tabsData = [];
}

6.Demo

Aaand we are done once again. Here is the final result:

7.Conclusion

This was the third tutorial of the AngularJs Bootstrap Components series This time showed you how to build tabs directive using Twitter’s Bootstrap Framework.

In the next tutorial of the series I’ll show you how to build Modal Directive based on Bootstrap’s Modals JS widget

Ivan Kovachev
Follow me

Ivan Kovachev

Ivan Kovachev is Technical Team Lead and Senior Web Developer with over six years of professional experience in the field. Ivan also has an unhealthy interest in everything web related.
Ivan Kovachev
Follow me