简体   繁体   中英

angular-ui-router and angular-ui-bootstrap tabs: content being loaded into DOM multiple times

I'm using angular-ui-bootstrap tabs and angular-ui-router. I want to load specific states into the tab-pane when a tab is clicked. This is working fine. The problem is that the state is being loaded into every tab pane in the DOM. Although the user can never tell, it is not a good idea to have all this content to be unnecessarily repeated in the DOM. The reason why this is happening is clear--the state templates are being inserted into ui-view, as can be seen below:

<tabset>
  <tab class="clinical-tabs" ng-repeat="tab in tabs" heading="{{tab.title}}" active="tab.active" disabled="tab.disabled" ui-sref="activity.clinical.{{tab.title}}">
    <div ui-view></div>
  </tab>
</tabset>

And the result is that the content is being loaded in every tab-pane:

<div class="tab-content">
  <!-- ngRepeat: tab in tabs -->
  <div class="tab-pane ng-scope active" ng-repeat="tab in tabs" ng-class="{active: tab.active}" tab-content-transclude="tab">
    <!-- uiView:  -->
    <div ui-view="" class="ng-scope">
      CONTENT IS LOADED HERE
    </div>
  </div>
  <!-- end ngRepeat: tab in tabs -->

  <div class="tab-pane ng-scope" ng-repeat="tab in tabs" ng-class="{active: tab.active}" tab-content-transclude="tab">
    <!-- uiView:  -->
    <div ui-view="" class="ng-scope">
      CONTENT IS ALSO LOADED HERE
    </div>
  </div>
  <!-- end ngRepeat: tab in tabs -->
  ...
  ...
  ...
</div>

Like I said, the cause of the problem is clear. However, I don't have enough experience with Angular to know how to come up with a solution.

While I'm not familiar with this tab directive, if you're looking to just have the content only be loaded once, you can make use of ngRepeat 's $first special scoping variable, which is equal to true only for the first iteration.

Combine that with ngIf and you'd have the following:

<tabset>
  <tab class="clinical-tabs" ng-repeat="tab in tabs" heading="{{tab.title}}" active="tab.active" disabled="tab.disabled" ui-sref="activity.clinical.{{tab.title}}">
    <div ng-if="$first" ui-view></div>
  </tab>
</tabset>

Which would produce:

<div class="tab-content">
  <!-- ngRepeat: tab in tabs -->
  <div class="tab-pane ng-scope active" ng-repeat="tab in tabs" ng-class="{active: tab.active}" tab-content-transclude="tab">
    <!-- uiView:  -->
    <div ui-view="" class="ng-scope">
      CONTENT IS LOADED HERE
    </div>
  </div>
  <!-- end ngRepeat: tab in tabs -->

  <div class="tab-pane ng-scope" ng-repeat="tab in tabs" ng-class="{active: tab.active}" tab-content-transclude="tab">
  </div>
  <!-- end ngRepeat: tab in tabs -->
  ...
  ...
  ...
</div>

I've found a solution. The trick is to use multiple named views with ui-router

The HTML code should be changed such that the ui-view is now named (note ui-sref no longer needed):

<tabset>
  <tab class="clinical-tabs" ng-repeat="tab in tabs" heading="{{tab.title}}" active="tab.active">
    <div ui-view="{{tab.title}}"></div>
  </tab>
</tabset>

In the main module, the $stateProvider should be configured differently There is a nice tutorial for setting up the multiple named views with ui-router here . The structure should be similar to:

.state('[parent]', {
  url: '/[someurl]',
  views: {
    '': { //<--this is the main template
      templateUrl: 'views/[view].html',
      controller: '[controller]'
    },
    '[child]@[parent]': { //<--these are the nested, named views that correspond to each tab
      templateUrl: 'views/[view].html',
      controller: '[controller]'
    },
    '...

Now, instead of one template being injected into the DOM 5 times (for 5 tabs), each template will be injected into its correct ui-view so there are no duplicates.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM