jnthnjns
jnthnjns

Reputation: 8925

Dynamic template in AngularJS Directive (Not templatUrl)

I have seen the ability to have a dynamic templateUrl within a Directive in Angular but while researching I have yet to see a dynamic template.

.directive('col', function () {
    var template = '<div class="one" ng-transclude></div>';
    return {
        restrict: 'E',
        scope: {},
        transclude: true,
        replace: true,
        template: template,
        link: function (scope, ele, attrs) {
            if (attrs.two !== undefined) {
                template = '<div class="two" ng-transclude></div>';
            } else if (attrs.three !== undefined) {
                template = '<div class="three" ng-transclude></div>';
            } else {
                template = '<div class="one" ng-transclude></div>';
            }
            console.log(template);
        }
    };
})

HTML:

<col three>Three</col>
<col two>Two</col>
<col>Nothing</col>

The console shows appropriately:

<div class="three" ng-transclude></div>
<div class="two" ng-transclude></div>
<div class="one" ng-transclude></div>

However the output shows the default / initial <div class="one" ng-transclude></div>

Upvotes: 1

Views: 3952

Answers (3)

tazmaniax
tazmaniax

Reputation: 416

If the template is dynamically constructed based on the attributes then you can just supply a template function passing in the attributes

.directive('col', function () {
    return {
        restrict: 'E',
        scope: {},
        transclude: true,
        replace: true,
        template: function(element, attrs) {
            var template = null;

            if (attrs.two !== undefined) {
                template = '<div class="two" ng-transclude></div>';
            } else if (attrs.three !== undefined) {
                template = '<div class="three" ng-transclude></div>';
            } else {
                template = '<div class="one" ng-transclude></div>';
            }

            return template;
        },
        link: function (scope, element, attrs) {

        }
    };
})

If the template is dynamically constructed based on the model then it's bit more involved. Note that the transclusion is done explicitly rather than using the ng-transclude directive.

.directive('col', function () {
    return {
        restrict: 'E',
        scope: {
            myVariable: '='
        },
        transclude: true,
        replace: true,
        link: function (scope, element, attrs, nullController, transclude) {
            var template = null;

            if (scope.myVariable == 'two') {
                template = '<div class="two"></div>';
            } else if (scope.myVariable == 'three') {
                template = '<div class="three"></div>';
            } else {
                template = '<div class="one"></div>';
            }

            element.html(template);
            $compile(element.contents())(scope);

            transclude(scope.$parent, function(clone, scope) {
                element.children().append(clone);
            });
        }
    };
})

Upvotes: 0

Jason Goemaat
Jason Goemaat

Reputation: 29234

When you say 'template = 'xxx' in your link function it is referencing your template variable, not the property on the returned object. Try this:

.directive('col', function () {
    var result = {
        restrict: 'E',
        scope: {},
        transclude: true,
        replace: true,
        template: template,
        link: function (scope, ele, attrs) {
            if (attrs.two !== undefined) {
                result.template = '<div class="two" ng-transclude></div>';
            } else if (attrs.three !== undefined) {
                result.template = '<div class="three" ng-transclude></div>';
            } else {
                result.template = '<div class="one" ng-transclude></div>';
            }
            console.log(result.template);
        }
    };
    return result;
})

Upvotes: 0

iConnor
iConnor

Reputation: 20209

This is because the template is collected before the link function is fired, if your only trying to change the classes then just do something like this.

.directive('col', function () {
    var template = '<div class="{{class}}" ng-transclude></div>';
    return {
        restrict: 'E',
        scope: {},
        transclude: true,
        replace: true,
        template: template,
        link: function (scope, ele, attrs) {
            $scope.class = attrs.three ? 'three' : attrs.two ?'two' : attrs.one ? 'one' : '';
        }
    };
});

Other than that I don't know how else you would accomplish it.

Upvotes: 2

Related Questions