AngularJSThere are a lot of great sets of directives available that are based on Twitter Bootstrap's markup and CSS and AngularJS. Some examples are AngularStrap and AngularUI, but neither of them has a directive that supports the Bootstrap's tabs and lazy data loading. So when the page with the tab directive is initially loaded, all the data, even of the not-active tabs, are loaded.

Here is how I solved the lazy data loading with Twitter Bootstrap's markup and AngularJS.

Tabset/Tab directives

The most interesting part is the code, so here it is. At first sight these are pretty straightforward directives. Though you probably can't see where the lazy data loading is done, but it's there! I assume you know how to create directives so I will explain only how the lazy loading is done. If you want to know more about AngularJS directives you can visit the AngularJS docs.

The Directives - JavaScript

'use strict';

angular.module('bootstrap.tabset', [])
.directive('tabset', function () {
  return {
    restrict: 'E',
    replace: true,
    transclude: true,
    controller: function($scope) {
      $scope.templateUrl = '';
      var tabs = $scope.tabs = [];
      var controller = this;

      this.selectTab = function (tab) {
        angular.forEach(tabs, function (tab) {
          tab.selected = false;
        });
        tab.selected = true;
      };

      this.setTabTemplate = function (templateUrl) {
        $scope.templateUrl = templateUrl;
      }

      this.addTab = function (tab) {
        if (tabs.length == 0) {
          controller.selectTab(tab);
        }
        tabs.push(tab);
      };
    },
    template:
      '<div class="row-fluid">' +
        '<div class="row-fluid">' +
          '<div class="nav nav-tabs" ng-transclude></div>' +
        '</div>' +
        '<div class="row-fluid">' +
          '<ng-include src="templateUrl">' +
        '</ng-include></div>' +
      '</div>'
  };
})
.directive('tab', function () {
  return {
    restrict: 'E',
    replace: true,
    require: '^tabset',
    scope: {
      title: '@',
      templateUrl: '@'
    },
    link: function(scope, element, attrs, tabsetController) {
      tabsetController.addTab(scope);

      scope.select = function () {
        tabsetController.selectTab(scope);
      }

      scope.$watch('selected', function () {
        if (scope.selected) {
          tabsetController.setTabTemplate(scope.templateUrl);
        }
      });
    },
    template:
      '<li ng-class="{active: selected}">' +
        '<a href="" ng-click="select()">{{ title }}</a>' +
      '</li>'
  };
});

HTML

This is a HTML example of how you can use the directives.

<tabset>
  <tab title="Tab 1" template-url="/views/tab1.html"></tab>
  <tab title="Tab 2" template-url="/views/tab2.html"></tab>
  <tab title="Tab 3" template-url="/views/tab3.html"></tab>
</tabset>

What About Lazy Data Loading?

The trickiest part was to figure out the lazy loading part. There isn't a way in AngularJS to say that you want lazy loading enabled. I solved the lazy loading part by using Angular's ng-include directive. Each time a tab is clicked the selected property of that tab is changed and it will eventually invoke the setTabTemplate function that will set the src attribute of the ng-include directive. When a ng-include without a src attribute is loaded, AngularJS will load nothing. When the src attribute is set, it will load the template. In the case of the tabs it will load the template of the selected tab. This is how I solved the lazy loading part.

Finally

Though there is no out-of-the-box option in AngularJS for enabling lazy loading, you can achieve it by using the ng-include directive. I showed one approach but there are probably other approaches as well.

I have also added a working example that demonstrates that the templates are only loaded when they are needed. Please see angularjs-tabs-and-lazy-data-loading on Github.