AngularJS - How to use $routeParams in generating the templateUrl?
Asked Answered
I

8

97

Our application has 2-level navigating. We want to use AngularJS $routeProvider to dynamically provide templates to an <ng-view />. I was thinking of doing something along the lines of this:

angular.module('myApp', []).
config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/:primaryNav/:secondaryNav', {
        templateUrl: 'resources/angular/templates/nav/'+<<primaryNavHere>>+'/'+<<secondaryNavHere>>+'.html'
    });
}]);

I just don't know how to populate the parts within the <<>>. I know the primaryNav and secondaryNav get bound to the $routeParams, but how do I access $routeParams here in order to dynamically serve up the template?

Idel answered 18/7, 2012 at 5:19 Comment(0)
A
84

I couldn't find a way to inject and use the $routeParams service (which I would assume would be a better solution) I tried this thinking it might work:

angular.module('myApp', []).
    config(function ($routeProvider, $routeParams) {
        $routeProvider.when('/:primaryNav/:secondaryNav', {
            templateUrl: 'resources/angular/templates/nav/'+$routeParams.primaryNav+'/'+$routeParams.secondaryNav+'.html'
        });
    });

Which yielded this error:

Unknown provider: $routeParams from myApp

If something like that isn't possible you can change your templateUrl to point to a partial HTML file that just has ng-include and then set the URL in your controller using $routeParams like this:

angular.module('myApp', []).
    config(function ($routeProvider) {
        $routeProvider.when('/:primaryNav/:secondaryNav', {
            templateUrl: 'resources/angular/templates/nav/urlRouter.html',
            controller: 'RouteController'
        });
    });

function RouteController($scope, $routeParams) {
        $scope.templateUrl = 'resources/angular/templates/nav/'+$routeParams.primaryNav+'/'+$routeParams.secondaryNav+'.html';
    }

With this as your urlRouter.html

<div ng-include src="templateUrl"></div>
Ascites answered 18/7, 2012 at 7:0 Comment(8)
It would be nice to not have create the one line html file, but I guess it's not too bad. Thanks for the help!Idel
The problem why the former does not work is documented groups.google.com/forum/?fromgroups=#!topic/angular/qNi5lqm-Ps8. As injection targets to config() only providers are passed, not actual service instances such as $routePrams.Neurocoele
You actually don't have to create the "one-line html file" - just use the "template:" key instead of "templateUrl" and provide it with a string containing the html one-liner ;-)Lenis
Using this technique, how could the templates fetched from the server side be cached? i.e. the template determined by $scope.templateUrlBred
I think the templates are cached by default at least by the browser and possibly by angular as well docs.angularjs.org/api/ng.$templateCache. This may help too: groups.google.com/d/topic/angular/b3_sbR5iUIU/discussionAscites
Couldn't you just use template instead of templateUrl?Rois
This will actually work, but what about applying a controller for each template selected? Using this method will leave you with only one "master" controller: "RouteController"Columbous
if you set template to a function, you can pass $routeParams into that function: $routeProvider.when('/:pNav/:sNav', { template: fn($routeParams) { return $routeParams.pNav + '/' + $routeParams.sNav + '.html' } });. However, you can't dynamically define a controller this way :(Stalwart
S
131

This very helpful feature is now available starting at version 1.1.2 of AngularJS. It's considered unstable but I have used it (1.1.3) and it works fine.

Basically you can use a function to generate a templateUrl string. The function is passed the route parameters that you can use to build and return the templateUrl string.

var app = angular.module('app',[]);

app.config(
    function($routeProvider) {
        $routeProvider.
            when('/', {templateUrl:'/home'}).
            when('/users/:user_id', 
                {   
                    controller:UserView, 
                    templateUrl: function(params){ return '/users/view/' + params.user_id; }
                }
            ).
            otherwise({redirectTo:'/'});
    }
);

Many thanks to https://github.com/lrlopez for the pull request.

https://github.com/angular/angular.js/pull/1524

Symphonia answered 4/4, 2013 at 17:9 Comment(5)
This is now fully supported in 1.2 and it probably the best way: docs.angularjs.org/api/ngRoute/provider/$routeProviderAndrien
what about a dynamic controller based on the params?Griefstricken
Please, please (please!) tell me that this is possible to do with controllers...?Slain
how do you access the the controller from the template in this case (supplied by $routeProvider)? Normally, if the controller is bound by ng-controller="myController" directive, you can reference it myController as myCtrl. How do I define myCtrl in this case?Factious
@Factious I just had this issue too - the solution is to do something like $routeProvider.when("/foo", { controller : "FooController", controllerAs : "foo", templateUrl: "foo.html" });Balzac
A
84

I couldn't find a way to inject and use the $routeParams service (which I would assume would be a better solution) I tried this thinking it might work:

angular.module('myApp', []).
    config(function ($routeProvider, $routeParams) {
        $routeProvider.when('/:primaryNav/:secondaryNav', {
            templateUrl: 'resources/angular/templates/nav/'+$routeParams.primaryNav+'/'+$routeParams.secondaryNav+'.html'
        });
    });

Which yielded this error:

Unknown provider: $routeParams from myApp

If something like that isn't possible you can change your templateUrl to point to a partial HTML file that just has ng-include and then set the URL in your controller using $routeParams like this:

angular.module('myApp', []).
    config(function ($routeProvider) {
        $routeProvider.when('/:primaryNav/:secondaryNav', {
            templateUrl: 'resources/angular/templates/nav/urlRouter.html',
            controller: 'RouteController'
        });
    });

function RouteController($scope, $routeParams) {
        $scope.templateUrl = 'resources/angular/templates/nav/'+$routeParams.primaryNav+'/'+$routeParams.secondaryNav+'.html';
    }

With this as your urlRouter.html

<div ng-include src="templateUrl"></div>
Ascites answered 18/7, 2012 at 7:0 Comment(8)
It would be nice to not have create the one line html file, but I guess it's not too bad. Thanks for the help!Idel
The problem why the former does not work is documented groups.google.com/forum/?fromgroups=#!topic/angular/qNi5lqm-Ps8. As injection targets to config() only providers are passed, not actual service instances such as $routePrams.Neurocoele
You actually don't have to create the "one-line html file" - just use the "template:" key instead of "templateUrl" and provide it with a string containing the html one-liner ;-)Lenis
Using this technique, how could the templates fetched from the server side be cached? i.e. the template determined by $scope.templateUrlBred
I think the templates are cached by default at least by the browser and possibly by angular as well docs.angularjs.org/api/ng.$templateCache. This may help too: groups.google.com/d/topic/angular/b3_sbR5iUIU/discussionAscites
Couldn't you just use template instead of templateUrl?Rois
This will actually work, but what about applying a controller for each template selected? Using this method will leave you with only one "master" controller: "RouteController"Columbous
if you set template to a function, you can pass $routeParams into that function: $routeProvider.when('/:pNav/:sNav', { template: fn($routeParams) { return $routeParams.pNav + '/' + $routeParams.sNav + '.html' } });. However, you can't dynamically define a controller this way :(Stalwart
P
19

templateUrl can be use as function with returning generated URL. We can manipulate url with passing argument which takes routeParams.

See the example.

.when('/:screenName/list',{
    templateUrl: function(params){
         return params.screenName +'/listUI'
    }
})

Hope this help.

Pointer answered 3/12, 2014 at 8:22 Comment(0)
S
7

Alright, think I got it...

Little background first: The reason I needed this was to stick Angular on top of Node Express and have Jade process my partials for me.

So here's whatchya gotta do... (drink beer and spend 20+ hours on it first!!!)...

When you set up your module, save the $routeProvider globally:

// app.js:
var routeProvider
    , app = angular.module('Isomorph', ['ngResource']).config(function($routeProvider){

        routeProvider = $routeProvider;
        $routeProvider
            .when('/', {templateUrl: '/login', controller: 'AppCtrl'})
            .when('/home', {templateUrl: '/', controller: 'AppCtrl'})
            .when('/login', {templateUrl: '/login', controller: 'AppCtrl'})
            .when('/SAMPLE', {templateUrl: '/SAMPLE', controller: 'SAMPLECtrl'})
            .when('/map', {templateUrl: '/map', controller: 'MapCtrl'})
            .when('/chat', {templateUrl: '/chat', controller: 'ChatCtrl'})
            .when('/blog', {templateUrl: '/blog', controller: 'BlogCtrl'})
            .when('/files', {templateUrl: '/files', controller: 'FilesCtrl'})
            .when('/tasks', {templateUrl: '/tasks', controller: 'TasksCtrl'})
            .when('/tasks/new', {templateUrl: '/tasks/new', controller: 'NewTaskCtrl'})
            .when('/tasks/:id', {templateUrl: '/tasks', controller: 'ViewTaskCtrl'})
            .when('/tasks/:id/edit', {templateUrl: '/tasks', controller: 'EditTaskCtrl'})
            .when('/tasks/:id/delete', {templateUrl: '/tasks', controller: 'DeleteTaskCtrl'})
        .otherwise({redirectTo: '/login'});

});

// ctrls.js
...
app.controller('EditTaskCtrl', function($scope, $routeParams, $location, $http){

    var idParam = $routeParams.id;
    routeProvider.when('/tasks/:id/edit/', {templateUrl: '/tasks/' + idParam + '/edit'});
    $location.path('/tasks/' + idParam + '/edit/');

});
...

That may be more info than what was needed...

  • Basically, you'll wanna store your Module's $routeProvider var globally, eg as routeProvider so that it can be accessed by your Controllers.

  • Then you can just use routeProvider and create a NEW route (you can't 'RESET a route' / 'REpromise'; you must create a new one), I just added a slash (/) at the end so that it is as semantic as the first.

  • Then (inside your Controller), set the templateUrl to the view you want to hit.

  • Take out the controller property of the .when() object, lest you get an infinite request loop.

  • And finally (still inside the Controller), use $location.path() to redirect to the route that was just created.

If you're interested in how to slap an Angular app onto an Express app, you can fork my repo here: https://github.com/cScarlson/isomorph.

And this method also allows for you to keep the AngularJS Bidirectional Data-Bindings in case you want to bind your HTML to your database using WebSockets: otherwise without this method, your Angular data-bindings will just output {{model.param}}.

If you clone this at this time, you'll need mongoDB on your machine to run it.

Hope this solves this issue!

Cody

Don't drink your bathwater.

Slain answered 19/3, 2013 at 8:24 Comment(0)
S
3

I've added support for this in my fork of angular. It allows you to specify

$routeProvider
    .when('/:some/:param/:filled/:url', {
          templateUrl:'/:some/:param/:filled/template.ng.html'
     });

https://github.com/jamie-pate/angular.js/commit/dc9be174af2f6e8d55b798209dfb9235f390b934

not sure this will get picked up as it is kind of against the grain for angular, but it is useful to me

Staves answered 29/8, 2012 at 22:25 Comment(3)
Right on! Havent used it yet but REALLY need it. Are there any other caveats that I should watch out for? Also, any word on Angular implementing it? - I like to use the CDN.Slain
No idea, I never got the paperwork signed so this a dead end, but you could take the change and try to get it pulled in yourself (I no longer work at the company I was with when I did this, and they didn't pursue angularjs any further)Staves
Ok, thanks for the info and your contribution to the issue - I'll be pulling that and playing around with it in a minute.Slain
C
3

Router:-

...
.when('/enquiry/:page', {
    template: '<div ng-include src="templateUrl" onload="onLoad()"></div>',
    controller: 'enquiryCtrl'
})
...

Controller:-

...
// template onload event
$scope.onLoad = function() {
    console.log('onLoad()');
    f_tcalInit();  // or other onload stuff
}

// initialize
$scope.templateUrl = 'ci_index.php/adminctrl/enquiry/'+$routeParams.page;
...

I believe it is a weakness in angularjs that $routeParams is NOT visible inside the router. A tiny enhancement would make a world of difference during implementation.

Cataract answered 1/11, 2013 at 0:19 Comment(0)
T
1
//module dependent on ngRoute  
 var app=angular.module("myApp",['ngRoute']);
    //spa-Route Config file
    app.config(function($routeProvider,$locationProvider){
          $locationProvider.hashPrefix('');
          $routeProvider
            .when('/',{template:'HOME'})
            .when('/about/:paramOne/:paramTwo',{template:'ABOUT',controller:'aboutCtrl'})
            .otherwise({template:'Not Found'});
    }
   //aboutUs controller 
    app.controller('aboutCtrl',function($routeParams){
          $scope.paramOnePrint=$routeParams.paramOne;
          $scope.paramTwoPrint=$routeParams.paramTwo;
    });

in index.html

<a ng-href="#/about/firstParam/secondParam">About</a>

firstParam and secondParam can be anything according to your needs.

Tiossem answered 7/2, 2018 at 19:10 Comment(0)
S
0

I was having a similar issue and used $stateParams instead of routeParam

Slighting answered 27/9, 2014 at 2:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.