bobvan
bobvan

Reputation: 251

When changing route state via a dropdown, how can I set a tab as active in a sub view?

I've assembled an angularjs app with ui-bootstrap & ui-router which has some tabbed sub-views where 'index' has a 'ui-view', then for some views, a template with 'uib-tabset' and another 'ui-view' is loaded into the main 'ui-view'.

My tabset isn't rendered with a repeater, but I do have a 'tabs' array setup in the controller with 'active: false' for all 5 of them, for example:

$scope.tabs = [{ active: false }, { active: false }, { active: false }, { active: false }, { active: false }];

The views load OK and the tabs activate OK because they have the 'active' attrib, for example:

<uib-tab heading="Users" ng-click="go('/settings/users')" active="tabs[3].active"></uib-tab>

But I also have a ui-bootstrap dropdown in the header, which transitions to the views as well as fires an 'itemSelected' event, for example:

<li><a href="#/settings/users" ng-click="itemSelected(3)">Users</a></li>

$scope.itemSelected = function(item) {
    $rootScope.$broadcast("itemSelected", { tabIdx: item });

The 'tabs' controller responds to the broadcast & sets the active tab:

$scope.$on('itemSelected', function (event, args) {
    $scope.tabs[args.tabIdx].active = true;

This (tab activation via dropdown) works OK when I'm within the sub views, but when I first enter into the sub views, this process fails to set the active, I think because of the order things are loaded and become ready for manipulation.

I don't have a base controller, but have defined controllers for every view since I will need to be adding more behaviors for them.

In one of the sub view controllers (for example, sub view #3 'Users'), I added the tabs controller via the '$controller' service, for example:

$controller('TabsCtrl', { $scope: $scope })

With this I seem to get a handle on the '$scope.tabs', but am still not able to set the active tab, for example:

$scope.tabs[3].active = true;

I realize I might set up a base controller and do these things in there, but then I might be affecting just one tabset in scope where I might want more later.

Or I would need logic to check the route state and only set active tab in certain states.

Or I could set an 'activeTab' variable on the rootScope and do something with it in the tabs template or controller to make it active.

But I would rather leverage automatic binding in some way, or at least listen and respond to the state changes in a more correct angularjs way, without adding hacks or workarounds.

When changing route state via a dropdown, how can I set a tab as active in a sub view?

Upvotes: 0

Views: 554

Answers (1)

bobvan
bobvan

Reputation: 251

I suppose I could pass the tab state around in the URL then process it somehow. I noticed I could control the tabs from the same controller and view but since my drop down is in the index header, and my tabset is in a ui-view, they don't play well. I suppose there are ways to pass data into views, but the issue with what I had is that controllers clean up the scope data when transitioning routes, so I would lose my tab state no matter what I tried.

A more angularjs approach was to add a factory to the base app module, for example:

.factory('tabsFactory', function() {
    var tabsService = {};
    var _tabsArray = [{ active: false }, { active: false }, { active: false }, { active: false }, { active: false }];
    tabsService.activateTab = function(idx) {
        for(var i = 0; i < _tabsArray.length; ++i) {
            if (idx != i) {
                _tabsArray[i].active = false;
            } else {
            _tabsArray[i].active = true;
            }
        };
    };
    tabsService.getTabsArray = function() {
        return _tabsArray;
    }
    return tabsService;

Now my dropdown controller 'itemSelected' function sets the activeTab:

$scope.itemSelected = function(idx) {
    tabsFactory.activateTab(idx);

In my tabs controller, I don't need to use '$scope.$on' to react to the broadcast, I just get the tabs from the factory, and the component renders the model state automatically:

$scope.tabs = tabsFactory.getTabsArray();

Also, since each view/sub-view has a controller, I can accommodate browser refresh by activating the appropriate tab in each sub-view controller:

tabsFactory.activateTab(1);

With this arrangement, I have working tab behaviors no matter how I navigate around the app. Thx

Upvotes: 1

Related Questions