angular multiple routes sharing one controller

16,080

Solution 1

Yes, you can reuse controllers and views as often as you want. If I understand you correctly, you want different routes to use the same controller and view? That's easy. Additionally, you want to be able to pass variables to your controller when the route is triggered? Also easy. Here is an example that does not use ui-router.

angular.module('myApp').config(['$routeProvider', 'whiskeyList', appConfig]); 

function appConfig($routeProvider, wiskeyList) {
  $routeProvider
  .when('/scotch', {
    controller: 'whiskeyListCtrlr',
    resolve: {
      data: function(whiskeyList) {
        return whiskeyList.getWhiskeys();
      }
    }
  })
  .when('/irish', {
    controller: 'whiskeyListCtrlr',
    resolve: {
      data: function(whiskeyList) {
        return whiskeyList.getWhiskeys();
      }
    }
  });
}

Obviously, this implementation is not DRY (Don't Repeat Yourself). I would rethink your implementation. I would change whiskeyList.getWhiskeys() to accept a parameter that tells it the type of whiskey to return. For example, whiskeyList.getWhiskeys('scotch'). Then, the data you receive in your controller is filtered to only what the view requires.

The data mapped in the router's resolve function is accessed in the controller by name.

whiskyControllers.controller('whiskyListCtrlr', ['$scope', 'data', function ($scope, data) {
  $scope.whiskeys = data;
});

Solution 2

This is pretty easy to do with ui-router, which offers many advantages over the built in routing in angular. Ui Router allows you to encapsulate states by specifying a template and controller, and "resolve" data (based off of route or other parameters) which you can then pass into your controllers. Something like the following would work well for you:

angular.module('whiskyApp')
    .config(['$stateProvider', function($stateProvider) {
        $stateProvider
        .state('whiskyList', {

             // whisky-type is a catch all here - can be 'irish', 'japanese', anything.
             // can also use regex to restrict it a bit
             url: '/lists/:whiskyType',
             templateUrl: 'whisky-list-template.html',
             controller: 'whiskyListCtrl',

             // this is what gets passed to the controller
             resolve: {
                 type: ['$stateParams', function($stateParams) {

                     // pass the whisky type from the url into the controller
                     return $stateParams.whiskyType;
                 }],
                 list: ['$stateParams', 'whiskyList', function($stateParams, whiskyList) {

                     // we can also go ahead and pass in the resolved list - this is best practice
                     return whiskyList.getWhiskies();
                 }]
             }
        });
    }]);

]);

And then in your contoller, just inject the name of keys of the 'resolve' map to use them:

whiskyControllers.controller('whiskyListCtrlr', ['$scope', 'list', 'type' '$http', 

    function($scope, list, type, $http){
       $scope.whiskies = list;
       $scope.type = type;
    }
])
Share:
16,080
Mike Alizade
Author by

Mike Alizade

Updated on June 05, 2022

Comments

  • Mike Alizade
    Mike Alizade almost 2 years

    I'm not sure if I'm approaching this correctly but I'm building an ecommerce site - part of the site has 6 different product grid pages, each of which can use the same view:

    <ul class="products row">
        <li class="product thumbnail col-1 grid" ng-repeat="whisky in whiskies | orderBy: sort">
            <p class="picture">
                <a ng-href="#/json/{{whisky.id}}"><img ng-src="images/scotch/{{whisky.imageUrl}}" alt="{{whisky.name}}"/></a>
            </p>
            <div class="desc">
                <h2>{{whisky.name}}</h2>
                <p class="price"><span>&pound;{{whisky.price}}</span></p>
            </div>
            <div class="actions">    
            <button class="add-to-basket btn btn-primary btn-medium fa fa-shopping-cart" data-item='{"imageUrl" : "{{whisky.imageUrl}}", "price" : "{{whisky.price}}", "startPrice" : "{{whisky.price}}", "name" : "{{whisky.name}}", "totalItems" : "{{1}}"}' ng-click="updateMiniBasket($event)"></button>    
            </div>
        </li>
    </ul>
    

    and the same controller:

    whiskyControllers.controller('whiskyListCtrlr', ['$scope', 'whiskyList', '$http', 
    
        function($scope, whiskyList, $http){
    
            whiskyList.getWhiskies().then(function(d) {
                $scope.whiskies = d.data;
            })
    
        }
    
    ])
    

    but need to have a different route in the route provider config i.e. one goes to scotch, one goes to irish, one to japanese etc.

    How do I code the routing so that the different pages share the same controller and view? Is it maybe possible to pass parameters from the router to the controller?

    Many thanks

  • Mike Alizade
    Mike Alizade over 9 years
    sorry had a problem with the other comment Thanks for this, but I can't get it to work. so far I have: var whiskyrumApp = angular.module('whiskyrumApp', ['ngRoute', 'ngResource', 'whiskyControllers']).config(['$routeProvider', 'whiskyList', appConfig]); I've added this when to your appconfig function when('/irish', { templateUrl : 'views/whisky-list.html', controller : 'whiskyListCtrlr', resolve : { data : function() { return whiskyList.getWhiskies('irish'); } } ). and have added the data params to the controller, but it complains that whiskyList is not available.
  • Brett
    Brett over 9 years
    @MikeAlizade How is whiskeyList implemented? Is it a service or factory? Are the parameters on your config function correct?
  • Mike Alizade
    Mike Alizade over 9 years
    the problem is that the whiskylist parameter (referring to a service) passed in the config function in my app.js is not recognised: var whiskyrumApp = angular.module('whiskyrumApp', ['ngRoute', 'ngResource', 'whiskyControllers']).config(['$routeProvider', 'whiskyList', appConfig]); this is because at this point, services.js is not loaded because it is called after app.js but if I put services.js above app.js, the services, which are called like this whiskyrumApp.factory(... won't recognise the whiskyrumApp var Not sure how to restructure it,or if I should use a factory or a service. Thx
  • Brett
    Brett over 9 years
    @MikeAlizade Ah, yes. Take a look at these answers. See if that helps.
  • Mike Alizade
    Mike Alizade over 9 years
    thanks for your help but chose the non ui-router method below