0

The AngularJs documentation says you can specify either a string or a function for controller & templateUrl in the route object when configuring the $routeProvider, however I'm running into an issue when using a function to dynamically determine the controller based on $location parameters. I have the following route configuration:

$routeProvider
       .when( '/workspace/:workspaceId/product/:productId/item/:itemType/:itemId/edit', {
        templateUrl: function ( param ) {
          switch ( param.itemType ) {
            case 'topic':
              return 'topic.tpl.html';
            case 'course':
              return 'course.tpl.html';
          }
          throw new Error( "Unknown product item type '" + param.itemType + "'" );
        },
        controller: function ( param ) {
          switch ( param.itemType ) {
            case 'topic':
              return 'TopicController';
            case 'course':
              return 'CourseController';
          }
          throw new Error( "Unknown product item type '" + param.itemType + "'" );
        }
      } );

When loading the application I get the following error:

Error: [$injector:unpr] Unknown provider: paramProvider <- param

Am I missing anything obvious here? Switching the controller to use a string rather than a function fixes the issue.

Looks like some people have run into this issue before (eg. here), but a I'm confused why this doesn't work as the documentation suggests that it should.

Any help would be greatly appreciated!

Joseph.

Community
  • 1
  • 1
Joseph Paterson
  • 1,443
  • 4
  • 18
  • 23
  • Where is param coming from and what are you trying to do? why can't you create two routes for them rather than having a dynamic route? – yangli-io Feb 10 '15 at 22:11
  • I thought Angular provided it (the same way as for templateUrl)? Creating two routes is definitely an option, but I thought I could also do it this way. – Joseph Paterson Feb 10 '15 at 22:17
  • Is `param` perhaps meant to be `$routeParams`? – JcT Feb 10 '15 at 22:17
  • Setting it to $routeParams gets rid of the error but the named controller doesn't load. Looks like if you supply a function then Angular is trying to use that function as the controller, rather than using it to supply the controller name – Joseph Paterson Feb 10 '15 at 22:19
  • try to add a () at the end of - function ( param ) { switch ( param.itemType ) { case 'topic': return 'TopicController'; case 'course': return 'CourseController'; }() – yangli-io Feb 10 '15 at 22:23
  • No luck with that either, I don't get the unknown provider error anymore but param is undefined (also tried with $routeParams & that's undefined too) – Joseph Paterson Feb 10 '15 at 22:37

1 Answers1

1

I think part of the issue may be that when you supply a function rather than a string to controller, it's not a function that's meant to return a controller name - it's instead a function that's meant to behave as the controller.

Have a glance at the following - the route/dynamic controller behaviour seems to work. I'm using $controller to instantiate the controller by name. So in this case, I'm not actually dynamically returning a different controller, I'm instantiating a new controller within the anon controller I've supplied to the $routeProvider.

Whether or not this is the best way to accomplish your intended end result might be a different matter. ;) The angular-ui/ui-router project is quite popular.

angular.module('myApp', ['ngRoute'])
  .config(function($routeProvider) {
    $routeProvider
      .when('/workspace/:workspaceId/product/:productId/item/:itemType/:itemId/edit', {
        template: '<h1>{{title}}</h1><p>{{test}}</p>',
        controller: function($scope, $routeParams, $controller) {
          switch ($routeParams.itemType) {
            case 'topic':
              $controller('TopicController', {
                $scope: $scope
              });
              break;
            case 'course':
              $controller('CourseController', {
                $scope: $scope
              });
              break;
          }
        }
      })
      .when('/', {
        template: '<h1>Home</h1><p>{{test}}</p>',
        controller: 'home'
      });
  }).controller('home', function($scope) {
    $scope.test = 'Hello world.';
  }).controller('TopicController', function($scope) {
    $scope.title = "Topic";
    $scope.test = 'Success!';
  }).controller('CourseController', function($scope) {
    $scope.title = "Course";
    $scope.test = 'Success again!';
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular-route.min.js"></script>
<div ng-app="myApp">
  <a href="#/workspace/1/product/1/item/topic/1/edit">Topic</a>
  <a href="#/workspace/1/product/1/item/course/1/edit">Course</a>
  <div ng-view />
</div>
JcT
  • 3,539
  • 1
  • 24
  • 34
  • I'm noticing that my answer is very similar to one in a link you provided: http://stackoverflow.com/a/22885416/446030. It's noted in a comment under this answer that apparently this can result in some issues with resolves, if you need them. So perhaps running with angular-ui/ui-router will give you more of the flexibility you need with fewer tradeoffs. – JcT Feb 10 '15 at 23:46
  • Thanks for your detailed answer! Will go with this for the time being & will also take a look into ui-router. – Joseph Paterson Feb 11 '15 at 02:43