title: AngularJS and UI-Router: keep controller loaded
body: <p>I am building a web application for our customer support. One of the needs is to be able to keep multiple tickets opened at the same time.<br>
  I was able to do the first part easily using a tabulation system and UI-Router.
  However, with my current implementation, each time I change active tab, the previously-current tab is unloaded, and the now-current tab is loaded (because it was unloaded with a previous tab change).</p>
  
  <p>This is not at all the expected behavior. I've already spent a couple of days trying to find a way to achieve this, without any luck.
  The closest thing I was able to do is to use the multiple views system from UI-Router, but I need multiple instance of the same view to keep in memory (if multiple tickets are opened, they all are on the same view, with the same controller, but a different scope)</p>
  
  <p>Here's my current implementation:
  supportApp.js:
      var app = angular.module("supportApp", ["ui.router", "ui.bootstrap"]);</p>
  
  <pre><code>app.config(function($stateProvider, $urlRouterProvider, $httpProvider){
      $urlRouterProvider.otherwise("/");
      $stateProvider
          .decorator('d', function(state, parent){
              state.templateUrl = generateTemplateUrl(state.self.templateUrl);
              return state;
          })
          .state("main", {
              abtract: true,
              templateUrl: "main.html",
              controller: "mainController"
          })
          .state("main.inbox", {
              url: "/",
              templateUrl: "inbox.html",
              controller: "inboxController"
          })
          .state('main.viewTicket', {
              url: '/ticket/{id:int}',
              templateUrl: "viewTicket.html",
              controller: "ticketController"
          })
      ;
  });
  </code></pre>
  
  <p>mainController.js: (handles other stuff, minimal code here)</p>
  
  <pre><code>app.controller("mainController", function($rootScope, $http, $scope, $state, $interval){
      // Tabs system
      $scope.tabs = [
          { heading: "Tickets", route:"main.inbox", active:false, params:{} }
      ];
  
      var addTabDefault = {
          heading: '',
          route: null,
          active: false,
          params: null,
          closeable: false
      };
  
      $rootScope.addTab = function(options){
          if(!options.hasOwnProperty('route') || !options.route)
          {
              throw "Route is required";
          }
  
          var tabAlreadyAdded = false;
          for(var i in $scope.tabs)
          {
              var tab = $scope.tabs[i];
              if(tab.route == options.route &amp;&amp; angular.equals(tab.params, options.params))
              {
                  tabAlreadyAdded = true;
                  break;
              }
          }
          if(!tabAlreadyAdded)
          {
              $scope.tabs.push($.extend({}, addTabDefault, options));
          }
          if(options.hasOwnProperty('active') &amp;&amp; options.active === true)
          {
              $state.go(options.route, options.hasOwnProperty('params')?options.params:null);
          }
      };
  
      $scope.removeTab = function($event, tab){
          $event.preventDefault();
          if($scope.active(tab.route, tab.params))
          {
              $scope.go($scope.tabs[0].route, $scope.tabs[0].params);
          }
          $scope.tabs.splice($scope.tabs.indexOf(tab), 1);
      };
  
      $scope.go = function(route, params){
          $state.go(route, params);
      };
      $scope.active = function(route, params){
          return $state.is(route, params);
      };
  
      $scope.$on("$stateChangeSuccess", function() {
          $scope.tabs.forEach(function(tab) {
              tab.active = $scope.active(tab.route, tab.params);
          });
      });
  });
  </code></pre>
  
  <p>main.html:</p>
  
  <pre><code>&lt;div class="container-fluid" id="sav-container"&gt;
      &lt;div class="row-fluid"&gt;
          &lt;div class="col-lg-2"&gt;
              &lt;form role="form" id="searchForm" action="#"&gt;
                  &lt;div class="form-group has-feedback"&gt;
                      &lt;input class="form-control" type="search" /&gt;
                      &lt;span class="glyphicon glyphicon-search form-control-feedback"&gt;&lt;/span&gt;
                  &lt;/div&gt;
              &lt;/form&gt;
          &lt;/div&gt;
          &lt;div class="col-lg-10" id="support_main_menu"&gt;
              &lt;ul class="nav nav-tabs"&gt;
                  &lt;li ng-repeat="t in tabs" ng-click="go(t.route, t.params)" ng-class="{active: t.active, closeable: t.closeable}" style="max-width: calc((100% - 128px) / {{tabs.length}});"&gt;
                      &lt;a href class="nav-tab-text"&gt;
                          &lt;button ng-show="t.closeable" ng-click="removeTab($event, t)" class="close" type="button"&gt;&amp;times;&lt;/button&gt;
                          &lt;span&gt;{{t.heading}}&lt;/span&gt;
                      &lt;/a&gt;
                  &lt;/li&gt;
              &lt;/ul&gt;
          &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="row-fluid"&gt;
          &lt;div class="tab-content" ui-view&gt;&lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>It seems to me that what I ask is pretty standard, but I sadly couldn't find any usefull thing on the Internet</p>