12

Here's my initial implementation of ui.bootstrap tabs using ui.router:

<tabset> <tab heading="Tab1" select="$state.go('home.tab1')"> <div ui-view="forTab1"></div> </tab> <tab heading="Tab2" select="$state.go('home.tab2')"> <div ui-view="forTab2"></div> </tab> </tabset> 

Nicely simple, and it mostly works, but it has several problems.

First, it doesn't work if I enter a URL into the browser, e.g. ".../home/tab1". The issue I presume is that while ui.router may be routing to the correct view, it doesn't know to select that view's tab.

Second, if I try to leave the page for a state on a different page, it triggers an apparent bug in the tabs logic. As the tab set is being destroyed, it destroys the tabs one by one. When it destroys the currently selected tab, it appears to run the same logic that it would run if only that tab were being destroyed, not the entire tab set. This causes it to select one of the other not-yet-destroyed tabs, which makes ui.router go to that tab's state, which prevents me from leaving the page. (The fix presumably should be that destroying the tab set should not select any tabs during the destruction process.)

Third, some of the tab views may be complex, with many server hits during their construction. I don't want to destroy and recreate them every time I switch from one tab to another. I'd much rather have a tab view be created when that tab is first selected and then persist until I leave the page. Selecting a previously viewed tab should just make it visible again, not re-create it.

So, it looks like I need something more complex and sophisticated, but what? Thanks in advance for any suggestions.

Update: the ui.router FAQ turns out to have a question on how to implement tabs. It points to an "extras" package that looks promising. Hopefully there's a way to integrate with ui.bootstrap tabs. http://christopherthielen.github.io/ui-router-extras/#/sticky

Update: here are two implementations that work much better but still aren't quite right. The first one uses ui.bootstrap tabs:

<tabset> <tab heading="Products" ui-sref=".products" active="$state.includes('home.products')"></tab> <tab heading="Users" ui-sref=".users" active="$state.includes('home.users')"></tab> </tabset> <div ui-view="home.products" ng-show="$state.includes('home.products')"></div> <div ui-view="home.users" ng-show="$state.includes('home.users')"></div> 

The second is based on top.html from Chris Thielen's sticky-tabs example:

<div class="navbar navbar-default navbar-static-top" role="navigation"> <ul class="nav navbar-nav"> <li ng-class="{active: $state.includes('home.products')}" ><a ui-sref="home.products">Products</a></li> <li ng-class="{active: $state.includes('home.users')}" ><a ui-sref="home.users">Users</a></li> </ul> </div> <div class="tabcontent well-lg" ui-view="home.products" ng-show="$state.includes('home.products')" class="col-sm-6"></div> <div class="tabcontent well-lg" ui-view="home.users" ng-show="$state.includes('home.users')" class="col-sm-6"></div> 

Both of these avoid my first two problems above - going to a tab via URL works, and I can leave the page without triggering the tabs destroy bug (presumably because I'm not using "select=").

However, there's still a problem, and it happens with both versions. The home.users template has a ui-grid, and if I first go to the page with that tab selected, the grid is empty. I can see the client requesting the grid's data from the server, but it doesn't show it. If I go to the other tab and come back, then it shows the data (after re-fetching it). Worse, this means I can't make the home.users state sticky, because then it gets stuck in the no-data-showing mode.

8
  • 1
    The "sticky states" examples don't use ui-bootstrap, but show how you can implement tabs like you describe in ui-router. Adding bootstrap css styling to the "sticky states" example should be pretty easy.
    – Chris T
    CommentedOct 27, 2014 at 17:36
  • Thanks, Chris. I actually found it difficult to find the markup you're using in the example. I ended up looking at top.html in the source area. I've now tried it two new ways. The first uses ui.bootstrap tabs:CommentedOct 27, 2014 at 18:08
  • Oops, sent in that comment too soon, and ran out of time to correct it. Again: thanks, @ChrisT. I've been looking at the sticky tabs example. Please see my update above.CommentedOct 27, 2014 at 18:30
  • I suspect your ui-grid problem is unrelated to being inside a tab. Can you show your logic to populate the data in the ui-grid? Are you using a resolve?
    – Chris T
    CommentedOct 27, 2014 at 18:44
  • @ChrisT, I'm using a custom service that works like $resource: it returns an array immediately that it later fills asynchronously. I'm planning on changing that code to something that uses a promise, which I'm hoping will fix the problem.CommentedOct 28, 2014 at 20:00

3 Answers 3

23

The easiest way is to ignore all the fancy ui-bootstrap directives and just make use of the CSS. Then you can end up with something very simple:

<ul class="nav nav-tabs"> <li ui-sref=".state1" ui-sref-active="active"><a>State 1</a></li> <li ng-repeat="type in viewModel.types" ui-sref=".typeState({type:type})" ui-sref-active="active"><a>{{type}}</a></li> </ul> 

This will give the appearance of the tabs while maintaining a correct active states. The trouble with using the <tab-header> directive is that you can't cleanly initialize the active state from the ui-router $state, so you get multiple tabs highlighted on page load.

3
  • You can solve the default view problem with this (toptal.com/angular-js/…). In combination with this solution of using ui-sref-active is the easy way. It worked for me. Thanks.CommentedJul 3, 2015 at 16:38
  • 1
    Do you have a plunker demo for this?CommentedJan 10, 2017 at 17:47
  • Thank you, I abandoned the ui-bootstrap for issues I was having with both tabs being picked. This worked well.
    – Diesel
    CommentedJul 28, 2017 at 23:41
4

You might consider having a look at this project, ui router plus ui bootstrap tabs, which declares itself as

Idiot-proof tab panes with route support using Angular.js + Bootstrap 3 + UI Router

I haven't tried it as yet, but certainly looks promising.

1
  • Just tested and it was EXACTLY what I needed. State pointer to tab and automatic controller refreshing on tab select. Really simple to use and idiot-proof, hahaha.
    – Diosney
    CommentedApr 12, 2015 at 16:19
1

Just did this in a project of mine. This assumes that your routes are wired with ui.router. Hope it helps.

<uib-tabset active="active"> <uib-tab index="0" ui-sref ="tab1" heading="Tab1"></uib-tab> <uib-tab index="1" ui-sref="tab2" heading="Tab2"></uib-tab> </uib-tabset> 

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.