snaggs
snaggs

Reputation: 5713

Angular material mdTab and abstract route issue

I try to create separate state per md-tab and following example works fine:

<div ng-app="app">  
  <script type="text/ng-template" id="one.html">
    <div>This is first tab</div>
  </script>  
  <script type="text/ng-template" id="two.html">
    <div>This is second tab</div>
  </script>  

    <md-tabs>
    <md-tab md-on-select="transitionTo('tab1',{})">
      <md-tab-label>Tab1</md-tab-label>
    </md-tab>
    <md-tab md-on-select="transitionTo('tab2',{})">
      <md-tab-label>Tab2</md-tab-label>
    </md-tab>
  </md-tabs> 

  <br>
  <div ui-view></div>
</div>

JS

var app = angular.module('app',['ui.router','ngMaterial']);
app.run(function($rootScope, $state) {
  $rootScope.transitionTo = function(state, params) {
    $state.transitionTo(state, params, { location: true, inherit: true, relative: $state.$current, notify: true });
  }
  $rootScope.checkState = function (state) {
    return $state.current.name == state ? true : false;
  }

  $rootScope.$on('$stateChangeSuccess', 
  function(event, toState, toParams, fromState, fromParams){
    if($state.current.name == 'tab2') {
      $rootScope.tab = { selected : 1 }
    } else {
      $rootScope.tab = { selected : 0 }
    }
  })
});
app.config(function($stateProvider, $urlRouterProvider) {
  $stateProvider

    .state('tab1', {
        url : '/tab1',
        templateUrl : 'one.html',
      controller: 'AppCtrl'
    })
    .state('tab2', {
        url : '/tab2',
        templateUrl : 'two.html',
    controller: 'AppCtrl'
    })
  $urlRouterProvider.otherwise('/tab1');
});


app.controller('AppCtrl', function($scope, $mdDialog, $mdMedia) {
  console.log('AppCtrl');
  });

See DEMO in Codepen


The disadvantage of this example, on tab change i load again and again AppCtrl controller.

So my goal is to load controller once.

One of approaches I know is to use parent abstract state. Here we go:

First off, I create new file root.html with tabs:

<script type="text/ng-template" id="root.html">
    <md-tabs>
    <md-tab md-on-select="transitionTo('root.tab1',{})">
      <md-tab-label>Tab1</md-tab-label>
    </md-tab>
    <md-tab md-on-select="transitionTo('root.tab2',{})">
      <md-tab-label>Tab2</md-tab-label>
    </md-tab>
  </md-tabs>
  </script>

And my state provider should look like:

$stateProvider
  .state('root', {
        url : '/root',
        templateUrl : 'root.html',
    controller: 'AppCtrl'
    })
    .state('root.tab1', {
        url : '/tab1',
        templateUrl : 'one.html'
    })
    .state('root.tab2', {
        url : '/tab2',
        templateUrl : 'two.html'
    })
  $urlRouterProvider.otherwise('/root/tab1');

Here is a DEMO with this change

However it doesn't work. I load AppCtrl once only but I don't see tab contents, in other words I never call 'root.tab1' and 'root.tab2'

What I'm doing wrong here?

Any ideas?

Upvotes: 3

Views: 143

Answers (1)

troig
troig

Reputation: 7212

I think your second approach is fine. You don't see tab contents because you need to put a <md-tab-body> with the ui-view inside each <md-tab>. Example:

<md-tab md-on-select="transitionTo('root.tab1',{})">
   <md-tab-label>Tab1</md-tab-label>
   <md-tab-body>
      <div ui-view></div>
   </md-tab-body>
</md-tab>

I've forked your plunker and here you have a working example: Controller is loaded only once and tab contents are seen.

Hope it helps.

Upvotes: 1

Related Questions