During the development of the web page, it is often required to represent multiple contents within a tab control. But, like other control, AngularJS have own tab directive. But we can create a simple tab directive with the help of AngularJS.
For creating a tab directive, we first need to create a stylesheet file. For this, we first add a style file called style.css and add the following code :-
  1. <style>
  2. .leftTab {width: 200px;float: left;padding: 0 7px;}
  3. .leftTab li {float: left;width: 100%;}
  4. .leftTab li a {height: 30px;font-size: 12px;display: block;text-align: left;padding: 3px 2px;letter-spacing: 1px;}
  5. .leftTab li a span {line-height: 25px;}
  6. .leftTab li a .iconArrow {font-size: 9px;height: 10px;margin: 9px 9px 0 0;width: 10px;}
  7. .leftTab li .tabSelect {font-weight: 700;}
  8. </style>
Now, first create the angular app as below :-
  1. var CustomCtrlApp = angular.module('CustomCtrlApp', []);
Now,Tab control required two different directives. One for heading related purpose and another represent the contains of that particular tab. So first we create two different html file named Tab.html and Tabset.html as in the following:
Tabset.html
  1. <div>
  2. <div id="leftTab" class="leftTab" ng-style="BodyStyle">
  3. <ul ng-transclude></ul>
  4. </div>
  5. <div class="tab-content multiPage" ng-style="BodyStyle">
  6. <div class="tab-pane" ng-repeat="fvtab in tabs" ng-class="{active: fvtab.active}" fv-tab-content-transclude="fvtab">
  7. </div>
  8. </div>
  9. </div>
Tab.html
  1. <li ng-class="{active: active, disabled: disabled}" ng-show="visible">
  2. <a ng-click="select()" fv-tab-heading-transclude ng-class="{'tabSelect' : active}">
  3. <i class="iconArrow left fv-right"></i><span class="left">{{heading}}</span>
  4. </a>
  5. </li>
Now, add a JavaScript file named Tab.js and write down the following code here:
  1. CustomCtrlApp.service('TabService', ['$rootScope', function ($rootScope) {
  2. var tabsetScope = null;
  3. var hideIndex = null;
  4. var hide = function (index) {
  5. var selectedTab = tabsetScope.tabs[index];
  6. hideIndex = null;
  7. tabsetScope.hideTab(selectedTab);
  8. }
  9. this.setTabsetScope = function (rootScope) {
  10. tabsetScope = rootScope;
  11. if (hideIndex != #ff0000) {
  12. hide(hideIndex);
  13. }
  14. }
  15. this.selectTab = function (index) {
  16. var selectedTab = tabsetScope.tabs[index];
  17. tabsetScope.select(selectedTab);
  18. hideIndex = null;
  19. }
  20. this.hideTab = function (index) {
  21. if (tabsetScope == undefined) {
  22. hideIndex = index;
  23. }
  24. else {
  25. hide(index);
  26. hideIndex = null;
  27. }
  28. }
  29. this.showTab = function (index) {
  30. hideIndex = null;
  31. var selectedTab = tabsetScope.tabs[index];
  32. tabsetScope.showTab(selectedTab);
  33. }
  34. }]);
  35. CustomCtrlApp.directive("tabset", [function ($httpservice) {
  36. this;
  37. return {
  38. restrict: 'EA',
  39. transclude: true,
  40. replace: true,
  41. scope: {
  42. type: '@'
  43. },
  44. templateUrl: "../HTMLTemplate/Tabset.html",
  45. controller: function ($scope, $element, $attrs, TabService) {
  46. var ctrl = this, tabs = ctrl.tabs = $scope.tabs = [];
  47. $scope.BodyStyle = getElementMinHeight();
  48. ctrl.select = function (selectedTab) {
  49. angular.forEach(tabs, function (tab) {
  50. if (tab.active && tab !== selectedTab) {
  51. tab.active = false;
  52. tab.visible = true;
  53. tab.onDeselect();
  54. }
  55. });
  56. selectedTab.active = true;
  57. selectedTab.onSelect();
  58. TabService.setTabsetScope(ctrl);
  59. };
  60. ctrl.hideTab = function (selectedTab) {
  61. if (selectedTab != undefined) {
  62. selectedTab.visible = false;
  63. TabService.setTabsetScope(ctrl);
  64. }
  65. };
  66. ctrl.showTab = function (selectedTab) {
  67. selectedTab.visible = true;
  68. TabService.setTabsetScope(ctrl);
  69. };
  70. ctrl.addTab = function addTab(tab) {
  71. tabs.push(tab);
  72. // we can't run the select function on the first tab
  73. // since that would select it twice
  74. if (tabs.length === 1 && tab.active !== false) {
  75. tab.active = true;
  76. } else if (tab.active) {
  77. ctrl.select(tab);
  78. }
  79. else {
  80. tab.active = false;
  81. }
  82. };
  83. ctrl.removeTab = function removeTab(tab) {
  84. var index = tabs.indexOf(tab);
  85. //Select a new tab if the tab to be removed is selected and not destroyed
  86. if (tab.active && tabs.length > 1 && !destroyed) {
  87. //If this is the last tab, select the previous tab. else, the next tab.
  88. var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;
  89. ctrl.select(tabs[newActiveIndex]);
  90. }
  91. tabs.splice(index, 1);
  92. };
  93. var destroyed;
  94. $scope.$on('$destroy', function () {
  95. destroyed = true;
  96. });
  97. }
  98. }
  99. }]);
  100. CustomCtrlApp.directive('TabContentTransclude', function () {
  101. return {
  102. restrict: 'A',
  103. require: '^tabset',
  104. link: function (scope, elm, attrs) {
  105. var tab = scope.$eval(attrs.TabContentTransclude);
  106. //Now our tab is ready to be transcluded: both the tab heading area
  107. //and the tab content area are loaded. Transclude 'em both.
  108. tab.$transcludeFn(tab.$parent, function (contents) {
  109. angular.forEach(contents, function (node) {
  110. if (isTabHeading(node)) {
  111. //Let tabHeadingTransclude know.
  112. tab.headingElement = node;
  113. } else {
  114. elm.append(node);
  115. }
  116. });
  117. });
  118. }
  119. };
  120. function isTabHeading(node) {
  121. return node.tagName && (
  122. node.hasAttribute('tab-heading') ||
  123. node.hasAttribute('data-tab-heading') ||
  124. node.tagName.toLowerCase() === 'tab-heading' ||
  125. node.tagName.toLowerCase() === 'data-tab-heading'
  126. );
  127. }
  128. });
  129. CustomCtrlApp.directive("tab", [function ($httpservice) {
  130. this;
  131. return {
  132. require: '^tabset',
  133. restrict: 'EA',
  134. replace: true,
  135. templateUrl: "../HTMLTemplate/Tab.html",
  136. transclude: true,
  137. scope: {
  138. active: '=?',
  139. heading: '@',
  140. onSelect: '&select', //This callback is called in contentHeadingTransclude
  141. //once it inserts the tab's content into the dom
  142. onDeselect: '&deselect'
  143. },
  144. controller: function () {
  145. //Empty controller so other directives can require being 'under' a tab
  146. },
  147. compile: function (elm, attrs, transclude) {
  148. return function postLink(scope, elm, attrs, tabsetCtrl) {
  149. scope.$watch('active', function (active) {
  150. if (active) {
  151. tabsetCtrl.select(scope);
  152. }
  153. });
  154. scope.disabled = false;
  155. scope.visible = true;
  156. if (attrs.disable) {
  157. scope.$parent.$watch($parse(attrs.disable), function (value) {
  158. scope.disabled = !!value;
  159. });
  160. }
  161. // Deprecation support of "disabled" parameter
  162. // fix(tab): IE9 disabled attr renders grey text on enabled tab #2677
  163. // This code is duplicated from the lines above to make it easy to remove once
  164. // the feature has been completely deprecated
  165. if (attrs.disabled) {
  166. $log.warn('Use of "disabled" attribute has been deprecated, please use "disable"');
  167. scope.$parent.$watch($parse(attrs.disabled), function (value) {
  168. scope.disabled = !!value;
  169. });
  170. }
  171. scope.select = function () {
  172. if (!scope.disabled) {
  173. scope.active = true;
  174. }
  175. };
  176. tabsetCtrl.addTab(scope);
  177. scope.$on('$destroy', function () {
  178. tabsetCtrl.removeTab(scope);
  179. });
  180. //We need to transclude later, once the content container is ready.
  181. //when this link happens, we're inside a tab heading.
  182. scope.$transcludeFn = transclude;
  183. };
  184. }
  185. }
  186. }]);
  187. CustomCtrlApp.directive('TabHeadingTransclude', [function () {
  188. return {
  189. restrict: 'A',
  190. require: '^tab',
  191. link: function (scope, elm, attrs, tabCtrl) {
  192. scope.$watch('headingElement', function updateHeadingElement(heading) {
  193. if (heading) {
  194. elm.html('');
  195. elm.append(heading);
  196. }
  197. });
  198. }
  199. };
  200. }]);
Actually, in the above AngularJS code we use ng-transclude to fulfill the desired objective. Actually, ng-transclude is performed as a setting which actually tell angular to capture every DOM element that is placed inside the directive in the html and use where ng-transclude is used.
Now, add another html file named TabDemo.html and write down the following code:
  1. <!DOCTYPE html>
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <title>Sample of Tab Directive Demostration</title>
  5. <style>
  6. .leftTab {width: 200px;float: left;padding: 0 7px;}
  7. .leftTab li {float: left;width: 100%;}
  8. .leftTab li a {height: 30px;font-size: 12px;display: block;text-align: left;padding: 3px 2px;letter-spacing: 1px;}
  9. .leftTab li a span {line-height: 25px;}
  10. .leftTab li a .iconArrow {font-size: 9px;height: 10px;margin: 9px 9px 0 0;width: 10px;}
  11. .leftTab li .tabSelect {font-weight: 700;}
  12. </style>
  13. <script src="../Scripts/angular.min.js"></script>
  14. <script src="../DirectiveScript/Tab.js"></script>
  15. </head>
  16. <body>
  17. <tabset>
  18. <tab heading="First Tab">
  19. This is First Tab
  20. </tab>
  21. <tab heading="Second Tab">
  22. This is Second Tab
  23. </tab>
  24. </tabset>
  25. </body>
  26. </html>