Can you change templateUrl on the fly?

30,080

Solution 1

It is possible, but when your template to be loaded depends on some scope-data you can't use the directive's templateUrl property anymore and you will be obliged to use lower-level API, namely $http and $compile.

Roughly what you need to do (only possible in the linking function) is to retrieve template's content using $http (don't forget to involve $templateCache!) and then compile template's content "manually".

It might sound like it is a lot of work but in practice it is rather straightforward. I would suggest having a look at the ngInclude directive sources where this pattern is used.

Here is a skeleton of such a directive:

app.directive('boom', function($http, $templateCache, $compile, $parse) {
        return {
            restrict: 'E',
            link: function(scope , iElement, iAttrs) {                            
              var boom = $parse(iAttrs.data)(scope);
              $http.get('myTemplate'+boom, {cache: $templateCache}).success(function(tplContent){
                iElement.replaceWith($compile(tplContent)(scope));                
              });              
            } 
        }
    });

assuming that it would be used as <boom data='name'></boom>. Working plunk here: http://plnkr.co/edit/TunwvhPPS6MdiJxpNBg8?p=preview

Please note that I've changed attributes evaluation from {{name}} to attributes parsing since probably a template should be determined only once, at the beginning.

Solution 2

This is a new feature in Angular versions 1.1.4+ I just found out if I use the current unstable (1.1.5) you can pass a function into the template url of a directive. The second parameter of the function is the value of the attribute directive as shown below.

Here is a link to the unpublished docs showing the official change.

To use partials/template1.html as the template url from

Html:

<div sub_view="template1"></div>

Directive:

.directive('subView', [()->
  restrict: 'A'
  # this requires at least angular 1.1.4 (currently unstable)
  templateUrl: (notsurewhatthisis, attr)->
    "partials/#{attr.subView}.html"
])

Solution 3

I had similar problem

 return {
        restrict: 'AE',
        templateUrl: function(elm,attrs){return (attrs.scrolled='scrolled' ?'parts/scrolledNav.php':'parts/nav.php')},
        replace: true,

partnersSite.directive('navMenu', function () {
    return {
        restrict: 'AE',
        templateUrl: function(elm,attrs){return (attrs.scrolled='scrolled' ?'parts/scrolledNav.php':'parts/nav.php')},
        replace: true,
        link: function (scope, elm, attrs) {
            scope.hidden = true;
            //other logics
        }
    };
});
<nav-menu scrolled="scrolled"></nav-menu>

Solution 4

This question will be fixed with ng-include as follow:

MyApp.directive('boom', function() {
    return {
      restrict: 'E',
      transclude: true,
      scope: 'isolate',
      locals: { data: 'bind' },
      templateUrl: '<div ng-include="templateUrl"></div>',
      link: function (scope) {
        function switchTemplate(temp) {
          if (temp == 'x')
          { scope.templateUrl = 'XTemplate.html' }
          else if (temp == 'y')
          { scope.templateUrl = 'YTemplate.html' }
        }
      }
    }
});

Call the switchTemplate function with arbitrary temp parameter in the link function of directive.

Solution 5

I've changed the answer from pkozlowski.opensource a little.

From:

var boom = $parse(iAttrs.data)(scope);

To:

var boom = scope.data.myData

That worked for me and it's possible to use

<boom data="{{myData}}" /> 

in the directive.

Share:
30,080

Related videos on Youtube

iLemming
Author by

iLemming

Updated on April 29, 2020

Comments

  • iLemming
    iLemming about 4 years

    Is it possible to change templateUrl on the fly by passing values in the directive's scope? I want to pass data to controller that will render the page based on the data that passed from the directive

    something maybe that looks like that:

    <div> 
       <boom data="{{myData}}" />
    </div> 
    
    .directive('boom', function {
            return {
                restrict: 'E',
                transclude: true,
                scope: 'isolate',
                locals: { data: 'bind' },
                templateUrl: "myTemplate({{boom}}})" // <- that of course won't work.
            }
        });
    
  • iLemming
    iLemming over 11 years
    yeah, i'm trying to play with ngInclude, but I can't find how to get value of local variable. How to get data in my case? I'm trying attrs.data, but it returns undefined
  • pkozlowski.opensource
    pkozlowski.opensource over 11 years
    Provided more info. I'm not sure if you really want to use interpolation of your attributes since it would mean that template can change dynamically as part of the $digest cycle. But if you really want to do so you would have to $observe attributes.
  • OpherV
    OpherV almost 11 years
    Solved my problem as well. Thanks!
  • EpiphanyMachine
    EpiphanyMachine almost 11 years
    the parameters are (element, attributes)
  • HMR
    HMR over 10 years
    I was using angular 1.0.8 and it may be worth mentioning that $compile fails when you didn't include jQuery before Angular.\
  • GFoley83
    GFoley83 about 10 years
    Example forked from Plnkr above: http://plnkr.co/edit/7BQxFEZ12Zxw9J9yT7hn. Note this approach doesn't work with Angular 1.0.8 (as used above) as even though iAttrs.data has a value (which you can seen if you log the iAttrs object), it's undefined when you try to access it.
  • AlexK
    AlexK almost 10 years
    you can't imagine how thankfull I am for this! I had a (similar) solution of my own, but combined with one of my directives, it just compiled two times... thank's so much!
  • andimeier
    andimeier almost 9 years
    It seems to work also, if you use var boom = scope.data; instead of using $parse... Why should I resort to $parse instead?
  • Ceasar Bautista
    Ceasar Bautista over 8 years
    That's not the issue at heart. If you actually create a demo app similar to the OP's, you'll see that you can't simply pass the interpolated value to the directive's templateURL function.
  • Joy George Kunjikkuru
    Joy George Kunjikkuru about 8 years
    Does this help to have <nav-menu scrolled="{{scrolled}}"></nav-menu> ie my attribute value will change during runtime and needs to have different template url based on that.