Toggle visibility of a ng-include depending on route
Asked Answered
T

4

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.

Tiertza answered 28/2, 2013 at 2:28 Comment(1)
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.Ashlan
S
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.

Studding answered 2/3, 2013 at 1:51 Comment(0)
P
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();
    };
}
Pericranium answered 23/6, 2014 at 20:42 Comment(0)
B
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

Bowing answered 28/2, 2013 at 2:42 Comment(2)
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.Tiertza
where/when do you change carsVisible and bikesVisible , and do you have a controller somewhere in the root index.html?Bowing
P
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;
});
Pericranium answered 28/2, 2014 at 10:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.