30

I have a directive myDirective with variable type. If I run <my-directive type="X"> I want the directive to use templateUrl: x-template.html. If I do <my-directive type="Y"> I want the directive to use templateUrl: y-template.html.

This is my current directive.

app.directive('myDirective', function() {
    var myDirective = {
        templateUrl: 'X-template.html',
        restrict: 'E',
        scope: {
            type: '=' 
        },
    };
    return myDirective;
});

I read thru stackoverflow and angular documentation but have not found anything that I need.

I am now trying to do something along the lines of:

if ($scope.type === 'X') {
    templateUrl: 'X-template.html',
}
else if ($scope.type === 'Y') {
    templateUrl: 'Y-template.html',
}

But do not know where to do it.

Do you guys know if this is possible and how?

Massive Boisson
  • 1,587
  • 6
  • 21
  • 28

6 Answers6

102

Angular will accept a function as the template option, so you could do something like so:

.directive('myDirective', function () {
    return {
        templateUrl: function (tElement, tAttrs) {
            if (tAttrs) {
                if (tAttrs.type === 'X') {
                    return 'X-template.html';
                }
                if (tAttrs.type === 'Y') {
                    return 'Y-template.html';
                }
            }
        }
    }
});

For more info, see the documentation for the $compile service.

Rob J
  • 6,514
  • 5
  • 26
  • 29
25

You can work around this issue using ng-include inside compile:

app.directive('myDirective', function() {
    return {
        restrict: 'E',
        compile: function(element, attrs) {
            element.append('<div ng-include="\'' + attrs.type + '-template.html\'"></div>');
        }
    }
});

fiddle

animuson
  • 50,765
  • 27
  • 132
  • 142
Mark Rajcok
  • 348,511
  • 112
  • 482
  • 482
  • @MarkRajcok How can i pass value to the templates ? suppose if i want to set attribute name="something" and pass this values in template ?? – anam Apr 30 '14 at 09:32
7

If you're willing to live on the bleeding edge with a build on the 1.1.x code path (note the warning attached to every 1.1.x build notes entry so I don't dilute this answer by repeating it again here), you're in luck--this very feature was just added in the 1.1.4 release on April 3rd. You can find the release notes for 1.1.4 here and the feature's task log includes a Jasmine test that demonstrates how to use the new functionality.

If you're more conservative and are using a 1.0.x release, then you won't be able to accomplish this as easily, but it can be done. Mark Rajcok's solution looks like it would fit your requirements as-stated, but I would just add a few additional notes:

  • Aside from its 1.1.4 release, compile-time directives don't support modification at runtime.
  • You may want to consider replaceWith() instead of append() since <my-directive> is not a standard-defined HTML element type.
  • If your X and Y templates contain additional directives, I don't think you'll be able to pass attributes on <my-template> through to the root element of your template so easily.
    • A directive with replace: true will transfer attributes from the source element to its replacement root, but I do not think that ngInclude will do the same from is host to the root of the included template.
    • I also seem to recall that ngInclude does not require that its template have exactly one root element.
    • You could perhaps preserve attributes on a replacement parent by using replaceWith() instead of append() and wrapping the <div ng-include=""> tag within a <div></div>. The outer <div> could hold attributes and would still be accessible after the <div ngInclude> element replaced itself with loaded content.
  • Be aware that ngInclude creates a new scope. This subjects you to a flashing yellow klaxons warning about the dangers of primitive scope models. For more information, see this fine page from Angular's GitHub depot.

I can propose another alternative for those on 1.0.x, but it involves a fair amount of code. It's a more heavy-weight operation, but it has the upside of not only being able of switching between templates, but full-fledged directives as well. Furthermore, its behavior is more readily dynamic.

app.directive('myDirective', function() {
    return {
        restrict: 'E',
        replace: true,
        templateUrl: 'partials/directive/my-directive.html',
        link: function(scope, element, attrs, ctrl) {
            // You can do this with isolated scope as well of course.
            scope.type = attrs.type;
        }
    }
);

my-directive.js

<div ng-switch on="{{type}}">
    <div ng-switch-where="X" ng-include="X-template.html"></div>
    <div ng-switch-where="Y" ng-include="Y-template.html"></div>
</div>

my-directive.html

John Heinnickel
  • 198
  • 1
  • 8
5

This is my version for optionally overriding a default template

templateUrl: function (elem, attrs) {
  if (attrs.customTemplate) {
    return '/path/to/components/tmpl/' + attrs.customTemplate + '.html';
  } else {
    return '/path/to/components/tmpl/directive.html';
  }
}

e.g on a directive

<div my-directive custom-template="custom"></div>
Joel Davey
  • 1,837
  • 1
  • 16
  • 15
4

I solve this problem so:

app.directive("post", function ($templateCache, $compile) {
function getTemplate(mode) {
    switch (mode) {
        case "create":
            return "createPost.html";
        case "view":
            return "viewPost.html";
        case "delete":
            return "deletePost.html";
    }
}

var defaultMode = "view";

return {
    scope: {},
    restrict: "AE",
    compile: function (elem, attrs, transclude) {
        return function ($scope, $element, $attr) {
            function updateTemplate() {
                $element.html("");
                $compile($templateCache.get(getTemplate($scope.mode)).trim())($scope, function (clonedElement, scope) {
                    clonedElement.appendTo($element);

                });
            }

            $scope.mode = $attr.mode || defaultMode;

            $scope.$watch("mode", updateTemplate);
        }
    }
}
});

It's probably not the best way to do this, but I have no extra scope.

Anton Rodin
  • 981
  • 2
  • 12
  • 19
-1

Ok, this might help someone here :-)

To inject your custom attr into your link or controller function use the following.

I'm at work right now but will post a fiddle later if I get a chance :-)

.directive('yourDirective', function() {
  return {
    restrict: 'EA',
    template: '<div></div>', // or use templateUrl with/without function
    scope: {
      myAttibute: '@myAttr' // adds myAttribute to the scope
    },
    link: function(scope) {
      console.log(scope.myAttibute);
    }
  }
}

// HTML ""

// Console will output "foo"

James
  • 1