7

I have the following configuration:

$routeProvider
.when('/cars', { templateUrl: 'app/cars/index.html', controller: 'CarsCtrl', reloadOnSearch: false })
.when('/bikes', { templateUrl: 'app/bikes/index.html', controller: 'BikesCtrl', reloadOnSearch: false });

and somewhere in my root index.html there is a:

<a href="#/cars">Cars</a>
<a href="#/bikes">Bikes</a>
<div ng-view></div>

Now, I want both views loaded and generated in the DOM at the same time, and show one of them depending on the route/URL.

Something like the following (not actual working code, just to give you an idea).

app.js:

$routeProvider
.when('/cars', { controller: 'CarsCtrl', reloadOnSearch: false })
.when('/bikes', { controller: 'BikesCtrl', reloadOnSearch: false });

root index.html:

<a href="#/cars">Cars</a>
<a href="#/bikes">Bikes</a>
<div ng-include="'app/cars/index.html'" ng-show="carsVisible"></div>
<div ng-include="'app/bikes/index.html'" ng-show="bikesVisible"></div>

UPDATE: I know that ng-view kind of does this, but the difference, if subtle, exists. I want the html of each view to be generated once and stay in the DOM at all times.

luisfarzati
  • 8,649
  • 6
  • 29
  • 27
  • It's not possible to accomplish this with ng-view. But the question is, why would you really want to do this. Is it because you would like to 'preprocess' each route so that it loads faster? Routing is already pretty fast, unless your controllers require some async data, in which case you would use 'resolve' inside the route definition object, plus some app initialization code for fetching async data in the background. – Stewie Feb 28 '13 at 08:48

4 Answers4

9

I created a single RouteCtrl to load all of your views via ng-include. ng-view is not used. I inlined the templates. The templates could contain their own ng-controller directives to pull in specific controllers.

<body ng-controller="RouteCtrl">
  <a href="#/cars">Cars</a>
  <a href="#/bikes">Bikes</a>
  <div ng-controller="RouteCtrl">
      <div ng-include="'/cars.html'"  ng-show="carsVisible"></div>
      <div ng-include="'/bikes.html'" ng-show="bikesVisible"></div>
  </div>

  <script type="text/ng-template" id="/cars.html">
     Cars template.
  </script> 
  <script type="text/ng-template" id="/bikes.html">
     Bikes template.
  </script> 

$routeProvider is still configured, but no template or controller is specified, causing the RouteCtrl to always be active. That controller listens for the $routeChangeSuccess event and manipulates the ng-show properties accordingly.

app.config(function($routeProvider) {
  $routeProvider
     .when('/cars', {} )
     .when('/bikes', {})
});

app.controller('RouteCtrl', function($scope, $route, $location) {
  $scope.$on('$routeChangeSuccess', function() {
    var path = $location.path();
    console.log(path);
    $scope.carsVisible = false;
    $scope.bikesVisible = false;
    if(path === '/cars') {
       $scope.carsVisible = true;
    } else if(path === '/bikes') {
       $scope.bikesVisible = true;
    }
  });
});

Plunker

The idea for this solution is from @Andy.

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
3

I found another way, which I think is the simplest, quickest and most manageable:

How to set bootstrap navbar active class with Angular JS?

Which is:

Use ng-controller to run a single controller outside of the ng-view:

<div class="collapse navbar-collapse" ng-controller="HeaderController">
    <ul class="nav navbar-nav">
        <li ng-class="{ active: isActive('/')}"><a href="/">Home</a></li>
        <li ng-class="{ active: isActive('/dogs')}"><a href="/dogs">Dogs</a></li>
        <li ng-class="{ active: isActive('/cats')}"><a href="/cats">Cats</a></li>
    </ul>
</div>
<div ng-view></div>

and include in controllers.js:

function HeaderController($scope, $location) 
{ 
    $scope.isActive = function (viewLocation) { 
        return viewLocation === $location.path();
    };
}
Community
  • 1
  • 1
Zymotik
  • 6,412
  • 3
  • 39
  • 48
1

Instead of using ng-include, you should use ng-view;

This will display the content of either app/cars/index.html or app/bikes/index.html

<a href="#/cars">Cars</a>
<a href="#/bikes">Bikes</a>
<div ng-view></div>

See the Template section from http://docs.angularjs.org/tutorial/step_07

Pascal Belloncle
  • 11,184
  • 3
  • 56
  • 56
  • Hi Pascal, thanks for your reply! However, this is not what I'm looking for. I made a clarification in my question. ng-view will toggle the contents, yes, but it does it by removing the contents from the exiting view and inserting the contents for the new view, each time the route changes. I want to have both contents already rendered and just toggle one or another with a ng-show or CSS display. – luisfarzati Feb 28 '13 at 02:49
  • where/when do you change `carsVisible` and `bikesVisible `, and do you have a controller somewhere in the root `index.html`? – Pascal Belloncle Feb 28 '13 at 02:51
1

Use a service with a show directive:

<div ng-show="myService.isActive('/')">Show this when at default route</div>
<div ng-show="myService.isActive('/cars')">Show this when at cars</div>

Service:

myApp.factory('MyService',
    function ($location) {
        return {
            isActive: function (path) {
                return (($location.path() == path));
            }
        }
    }
);

App.js:

// this is run after angular is instantiated and bootstrapped
myApp.run(function ($rootScope, myService) {
    $rootScope.breadcrumbService = MyService;
});
Zymotik
  • 6,412
  • 3
  • 39
  • 48