16

So as of Angular 1.1.4, you can have a dynamic template url. From here,

templateUrl - Same as template but the template is loaded from the specified URL. Because the template loading is asynchronous the compilation/linking is suspended until the template is loaded.

You can specify templateUrl as a string representing the URL or as a function which takes two arguments tElement and tAttrs (described in the compile function api below) and returns a string value representing the url.

How can I utilize this to generate a dynamic template based on, say, an attribute on my directive? Obviously this doesn't work, since tAttrs.templateType is simply the string "templateType"

templateUrl: function (tElement, tAttrs) {
  if (tAttrs.templateType == 'search') {
    return '/b/js/vendor/angular-ui/template/typeahead/typeahead.html'
  } else {
    return '/b/js/vendor/angular-ui/template/typeahead/typeahead2.html'
  }
}

Given that I don't have access to the scope, how do I manage this?

Community
  • 1
  • 1
Scott Silvi
  • 3,059
  • 5
  • 40
  • 63

4 Answers4

30

The following is also possible for creating dynamic templates in AngularJS: In your directive use:

template : '<div ng-include="getTemplateUrl()"></div>'

Now your controller may decide which template to use:

$scope.getTemplateUrl = function() {
  return '/template/angular/search';
};

Because you have access to your scope parameters, you could also do:

$scope.getTemplateUrl = function() {
  return '/template/angular/search/' + $scope.query;
};

So your server could create a dynamic template for you.

Ruud
  • 601
  • 7
  • 7
  • 15
    While this is an interesting, however, it seems to go against the grain of angular design, because it introduces a dependency between the directive and the controller which will dilute the encapsulation and standalone capability of the directive. – Jarnal Oct 10 '13 at 16:27
  • 1
    You could instead declare/assign the dynamic function inside of a `link:` property in the directive, since link can take a callback `function(scope, element, attrs)` that provides access to `scope`. This improves its ability to stand alone. – cjn Aug 14 '15 at 20:56
4
templateUrl: function (elem, attrs) {
return attrs["template"] == "table" ?
"tableTemplate.html" : "itemTemplate.html";
}
zloctb
  • 10,592
  • 8
  • 70
  • 89
  • 6
    While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. – ryanyuyu Sep 08 '15 at 14:18
1

So the issue was with how I hacked the typeahead directive ... I was setting a scope variable on the typeahead, to be evaluated on the typeaheadPopup directive. Instead, I just passed the templateType attr directly as string & evaluated that. E.g.

var popUpEl = angular.element(
  "<typeahead-popup " +
    "matches='matches' " +
    "active='activeIdx' " +
    "select='select(activeIdx)' " +
    "template-type='" + attrs.templateType + "'" +
    "query='query' " +
    "position='position'>" +
  "</typeahead-popup>");

Instead of "template-type='templateType'"

Scott Silvi
  • 3,059
  • 5
  • 40
  • 63
1

Ran into a similar issue when creating a file upload fallback for browsers that don't support the File API (< IE10). Key difference is I needed the page to intelligently decide which template to display without the benefit of an attribute value to switch on.

I ended up using the constant provider for my directive. Constants basically set up default parameters that can be injected anywhere in your directive. I simply let the constant call a function to determine browser support, then reference that value when I need to determine which template to pull. This is nice since 1) there's no attribute to reference and 2) it's available during the pre-link phase when you don't have access to the controller.

(function () {
  var myDir = angular.module('myDir', []);
  myDir.constant('myDirConfig', {
      hasFileSupport: fileApiIsSupported()
    });

  myDir.directive('myDir', ['myDirConfig', function (myDirConfig) {
      return {
          templateUrl: function () {
              if (myDirConfig.hasFileSupport) {
                  return 'pathToTemplate/html5.html';
              } else {
                  return 'pathToTemplate/fallback.html';
              }
          }
      };
  }];

  function fileApiIsSupported() { return (...); }
})();
iamsar
  • 1,080
  • 2
  • 15
  • 28