0

I'm trying to set a tab as active through the markup. For some reason when I set the active attribute on a tab it seems to mangle the state of the tabs. The page loads up fine and the tab that was set as active will be deactivated when clicking another tab. When I click back on the tag that was set with active="true" the previously selected tab will not be deselected.

...
<tab heading="Dynamic Title 1" active="true">Some Title 1</tab>
...

http://plnkr.co/edit/xzDbezXgkSMr6wokov6g?p=info

I switched to creating a variable that is set to true at init and plopped that into the active attribute. I'm hoping there's a better way to this though.

  <tabset ng-init="startActive = true">
    ...
    <tab heading="Dynamic Title 1" active="startActive">Some Title 1</tab>
    ...
  </tabset>

http://plnkr.co/edit/mt5MQSZEl730fsMuMxg8

I don't want to define the tabs in js because this is a project that uses webforms and piping data from that to js might be worse than what I'm doing here. I change the page to be completely built with angular in which case piping data like the tab to be selected could be part of some config endpoint that would be hit on the controller's init. I'd rather not have to redesign a complete page to make this change but it seems like the most correct way to go. Any thoughts and tips would be appreciated.

2 Answers 2

2

I know this is quite old, but after wasting hours of my life, I came up with a super dirty hack, that does the trick (assuming I understood your question correctly and you have the same issue as me).

Problem Statement

Using UI Bootstrap Tabs, dynamically adding tabs based on list data and maintaining the active state outside of this data.

When using the Tabs of UI Bootstrap and generating tabs like this:

<tab ng-repeat="item in page.data.list" active="item.active">

UI Bootstrap will use the binding of the item to store the active state. If we omit the active attribute, UI Bootstrap will maintain it internally but then it becomes impossible to manipulate the active state from the outside, except for accessing it via one of these: $$ (which are the untouchables!)

Solution (The Hack)

Maintain the active state in another list:

<div ng-controller="parasample-tabs">
{{activeState}}
<tabset ng-show="page.data.list.length">
    <tab ng-repeat="item in page.data.list" active="activeState[$index]">
                <tab-heading>
        <i style="cursor: pointer" class="glyphicon glyphicon-remove" ng-click="delTab($index)" prevent-default></i>
        Item {{$index +1}}
    </tab-heading>
    {{item.text}} - {{item.transcript}} - {{item.active}}
    </tab>
</tabset>
<!-- 
  For me this problem arose because I like to use self-contained, self-managing data 
  from factories, hence I call addItem not on a controller
-->
<button ng-click="page.addItem()">Add Item</button>
</div>

Now for the controller, that is wrapped around that tabs and manages them, and their active state instead of writing it into my data:

app.controller('parasample-tabs', function ($scope) {
    $scope.maxItems = 5;

    $scope.activeState = [];
    $scope.delTab = function (idx) {
        var list = $scope.page.data.list;
        if (list.length > 0) {
            $scope.page.delItem(idx);
            $scope.activeState.splice(idx, 1);
        }
    };
    $scope.$watch(
        "page.data.list.length",
        function (newLen, oldLen) {
            if (!newLen) return;
            // new item => new tab, make active
            if (newLen > oldLen)
                $scope.activeState.push("true");
        }
    );
});

Now UI Bootstrap will access the array activeState and store the active state there. There is no need for initialisation, as that is taken care of.

When a new item is added to our data list, the watch will set the new tab as the active tab (thats my preference) and the rest of the list will be updated by UI Bootstrap.

When deleting however, it is not easily possible to determine which item was removed, so I had to wrap my page.delItem into the controller's delTab method.

Here is a fiddle to play with, too.

Let's hope that UI Bootstrap will allow for a different way to maintain the active state instead of having a two way binding in the active attribute. I like having an "active ID" variable.

Disclaimer: I am aware of how dirty this is and I only tested it in Chrome so far and it works nicely.

Sign up to request clarification or add additional context in comments.

Comments

2

You're missing quite a few here. Here's a more extensible way:

var app = angular.module('myApp',[]);

app.controller('MyController', ['$scope',function($scope){
  $scope.tab = 0;
  
  $scope.changeTab = function(newTab){
    $scope.tab = newTab;
  };
  
  $scope.isActiveTab = function(tab){
    return $scope.tab === tab;
  };
  
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE HTML>
<html ng-app="myApp">
	<head>
      <style type="text/css">
          .active{
          background-color:red;
        }
        </style>
	</head>
	
	<body ng-controller="MyController">
		<div>
			<div ng-class="{'active':isActiveTab(0)}" ng-click="changeTab(0)">Some Title 1</div>
			<div ng-class="{'active':isActiveTab(1)}" ng-click="changeTab(1)">Some Title 2</div>
		</div>
		<br/>
		<div ng-show="isActiveTab(0)">tab1</div>
		<div ng-show="isActiveTab(1)">tab2</div>

		
		<script type="text/javascript" src="js/angular-1.2.24.min.js"></script>
		<script type="text/javascript" src="js/app.js"></script>
		
	</body>
</html>

  1. Initialization should always be in the controller.
  2. Change the values using a controller function. Here, defined as 'changeTab()'
  3. For checking active tabs, create a controller function to compare if the current value of $scope.tab is equal to the current tab.

I also added a bit of styling to impose which tab is active.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.