mgtrkg
Last Updated: February 25, 2016
·
79.9K
· devers
D27d0237833f17bd8c503b038c7bbcf2

Variable templates for an AngularJS directive

Sometimes web services and APIs return different data sets for the same type of object. For example, the tumblr API will return a list of posts, but each post type has different data associated with it: text posts have a title and body, while photo posts have captions and images.

Since having different components and directives for each post type doesn't make much sense (especially when they're all displayed in one stream, as on tumblr), it's beneficial to be able to conditionally load a template based on the post type of each individual post.

This code is based on these two articles/posts:
https://github.com/angular/angular.js/issues/1039#issuecomment-10673465
http://onehungrymind.com/angularjs-dynamic-templates/

and here is the tumblr API for reference: http://www.tumblr.com/docs/en/api/v2

components.js

angular.module('components', []).
    directive('tumblrPost', ['$compile', '$http', '$templateCache', function($compile, $http, $templateCache) {

        var getTemplate = function(contentType) {
            var templateLoader,
            baseUrl = '/templates/components/tumblr/',
            templateMap = {
                text: 'text.html',
                photo: 'photo.html',
                video: 'video.html',
                quote: 'quote.html',
                link: 'link.html',
                chat: 'chat.html',
                audio: 'audio.html',
                answer: 'answer.html'
            };

            var templateUrl = baseUrl + templateMap[contentType];
            templateLoader = $http.get(templateUrl, {cache: $templateCache});

            return templateLoader;

        }

        var linker = function(scope, element, attrs) {

            var loader = getTemplate(scope.post.type);

            var promise = loader.success(function(html) {
                element.html(html);
            }).then(function (response) {
                element.replaceWith($compile(element.html())(scope));
            });
        }

        return {
            restrict: 'E',
            scope: {
                post:'='
            },
            link: linker
        };
    }]);

news.html (all posts view)

<h1 class="road">News</h1>

<ul id="news">
    <li ng-repeat="tumblrPost in posts">
        <tumblr-post post="tumblrPost"></tumblr-post>
    </li>
</ul>

<p class="read-more"><a href="http://{{posts[0].blog_name}}.tumblr.com/" target="_blank">Read more news</a></p>

text.html (tumblr text post component)

<a href="{{post.post_url}}" target="_blank" class="post-date">{{post.date}}</a>
<h3>{{post.title}}</h3>
<div ng-bind-html="post.body"></div>
<hr>

video.html (tumblr video post component, for comparison)

<a href="{{post.post_url}}" target="_blank" class="post-date">{{post.date}}</a>
<div ng-bind-html-unsafe="post.player[0].embed_code" ui-jq="fitVids"></div>
<div ng-bind-html="post.caption"></div>
<hr>

10 Responses
Add your response

8315
546e65cc0efc25fafcce3974e10afc78

Hi Devers,

Thanks for your nice example.
It's great helpful to me.
But, if I don't want to replace the whole html of element, how to?
I have try to using "element.html($compile(element.html())(scope))"
But it doesn't work.
Could you give me some hint?
Thank you.

over 1 year ago ·
8317
546e65cc0efc25fafcce3974e10afc78

Hi Devers,

I have knew how to do.
I just using "$compile(element.contents())(scope)" to replace "element.replaceWith($compile(element.html())(scope))"
Thank you.

over 1 year ago ·
9000

Nice article, I was just working on something like this and this is showing me that Im on the right path.

Fran

over 1 year ago ·
9202
Dagu new head

Good read, I really like this pattern. It's pretty sweet!

over 1 year ago ·
9793
0 ip83md6ra82lyvg dkrommnt1hmdkvs dtnym7ka2isj7asibvkdcfrhjej91tu7okc0nh91hnui

Very useful, thanks ;)

over 1 year ago ·
10334
4f4122bc3b9999d9050f0b1a10b63251

Nice writeup. I'm making an angular directive where I can set the dynamic template. It works. Now I would love to update the template using broadcast events. The code works fine [1] and the new HTML is generated from $compile. The problem is that the directive does not 'refresh' the content. Any idea?

[1] https://gist.github.com/andreareginato/7171134

over 1 year ago ·
12634
Ba65285853652ee3bdd7e74c0d3b72e5

My Html binding for video template is not working Rest works like a charm :(

over 1 year ago ·
14778
V2iyuyki normal

Using element for accessing the DOM e.g element.find(".child-class") does not work because of the element.replaceWith(). Any cleaner solution?

over 1 year ago ·
14780
V2iyuyki normal

Nevermind. We can do something like this:


var compiled = $compile(response.data)(scope); element.replaceWith(compiled); element = compiled; </code> </pre> Thanks for the nice article though.
over 1 year ago ·
17327
None

Awesome example! Why are you doing both success and then? Looks like almost the same is happening in there?

over 1 year ago ·