This is going to be a really fun post for me. AngularJS is a super powerful framework for building great applications but I love it when I figure out ways to bring the fashion to the functionality.
The Backstory
I am working on a project that allows the user to post various types of content to a timeline and essentially the data structure is the same but the styling varies quite a bit. As a result I needed to be able to style each content item according to its content type.
The problem is that AngularJS does not have any logic built into its templates other than what you can get via binding. This is a good thing! A View should not have logic in it and only reflect the state of the Model. ViewModel I love you!
So how do get around this problem while keeping theoretical integrity in tact? $compile to the rescue!
Preview
http://onehungrymind.com/demos/angular-dynamic-templates/
Source
https://github.com/simpulton/angular-dynamic-templates
The Application
So lets get started with the actual data model we are working with.
[ {"content_type" : "image", "title" : "Image 00", "data" : "temp-photo.jpg"}, {"content_type" : "video", "title" : "Video 00", "data" : "http://player.vimeo.com/video/37176398"}, {"content_type" : "notes", "title" : "Notes 00", "data" : "Lorem ipsum dolor sit amet, consectetur adipiscing elit."} ]
It is a pretty straightforward data structure that shares title and data while differentiating itself on content_type.
And then we get the party started with a controller.
<div id="container" ng-controller="ContentCtrl"></div>
This next bit of code is for fetching the data and populating a content property that we are going to bind to in a template in just a moment.
Side note: I am using promises with my $http.get call and I love how concise and expressive promises make my code.
function ContentCtrl($scope, $http) { "use strict"; <pre><code>$scope.url = 'content.json'; $scope.content = []; $scope.fetchContent = function() { $http.get($scope.url).then(function(result){ $scope.content = result.data; }); } $scope.fetchContent(); </code></pre> }
And now that we have populated $scope.content with meaningful data, we are going to go ahead an loop over it and stamp out “something”.
<div id="container" ng-controller="ContentCtrl"> <content-item ng-repeat="item in content" content="item"></content-item> </div>
In this case, I am creating a directive called contentItem that I have big plans for. Seriously, I am so excited I have to keep from spoiling where I am going with this.
Lets cover real quick the skeleton that I have put in place. I am restricting this to an element which is merely personal preference but it feels more like a self contained widget that way. I am also creating an isolated scope that is bound to the content property. Again, more self contained awesomeness. And there is the linker function which is where we are going to tie everything together at the end.
app.directive('contentItem', function ($compile) { var linker = function(scope, element, attrs) { // DO SOMETHING } <pre><code>return { restrict: "E", link: linker, scope: { content:'=' } }; </code></pre> });
So we need templates! I am going to pull a move from the BackboneJS/Underscore playbook and store them as strings in the directive. I have three template strings with one for each type of content I am dealing with.
app.directive('contentItem', function ($compile) { var imageTemplate = ' <div class="entry-photo"> <h2> </h2> <div class="entry-img"><span><a href="{{rootDirectory}}{{content.data}}"><img ng-src="{{rootDirectory}}{{content.data}}" alt="entry photo"></a></span></div> <div class="entry-text"> <div class="entry-title">{{content.title}}</div> <div class="entry-copy">{{content.description}}</div> </div> </div> '; var videoTemplate = ' <div class="entry-video"> <h2> </h2> <div class="entry-vid"><iframe ng-src="{{content.data}}" width="280" height="200" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe></div> <div class="entry-text"> <div class="entry-title">{{content.title}}</div> <div class="entry-copy">{{content.description}}</div> </div> </div> '; var noteTemplate = ' <div class="entry-note"> <h2> </h2> <div class="entry-text"> <div class="entry-title">{{content.title}}</div> <div class="entry-copy">{{content.data}}</div> </div> </div> '; <pre><code>/* EDITED FOR BREVITY */ </code></pre> });
And then I have a simple utility function that returns the appropriate template based on type. This COULD be more concise but I opted for readability for illustration purposes.
app.directive('contentItem', function ($compile) { /* EDITED FOR BREVITY */ <pre><code>var getTemplate = function(contentType) { var template = ''; switch(contentType) { case 'image': template = imageTemplate; break; case 'video': template = videoTemplate; break; case 'notes': template = noteTemplate; break; } return template; } /* EDITED FOR BREVITY */ </code></pre> });
Behold! The Magic!
This is a one two punch. First, I get the appropriate template and add it to the DOM via element.html() and then show().
But! The newly minted template has not been endued with AngularJS powers yet. This is where we use the $compile service. And what exactly does $compile do? Thanks for asking! You are a lovely audience.
Compiles a piece of HTML string or DOM into a template and produces a template function, which can then be used to link scope and the template together.
Side note: there is also a property called rootDirectory that I have bene using lately to make it easier to switch environments depending on relative or absolute reference requirements for their assets.
app.directive('contentItem', function ($compile) { /* EDITED FOR BREVITY */ <pre><code>var linker = function(scope, element, attrs) { scope.rootDirectory = 'images/'; element.html(getTemplate(scope.content.content_type)).show(); $compile(element.contents())(scope); } /* EDITED FOR BREVITY */ </code></pre> });
Wrap Up
And now we have seen behind the curtain of a way to dynamically create templates based on conditions and then shooting them up with performance enhancing AngularJS drugs on the fly.
Here is the directive in its entirety.
app.directive('contentItem', function ($compile) { var imageTemplate = ' <div class="entry-photo"> <h2> </h2> <div class="entry-img"><span><a href="{{rootDirectory}}{{content.data}}"><img ng-src="{{rootDirectory}}{{content.data}}" alt="entry photo"></a></span></div> <div class="entry-text"> <div class="entry-title">{{content.title}}</div> <div class="entry-copy">{{content.description}}</div> </div> </div> '; var videoTemplate = ' <div class="entry-video"> <h2> </h2> <div class="entry-vid"><iframe ng-src="{{content.data}}" width="280" height="200" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe></div> <div class="entry-text"> <div class="entry-title">{{content.title}}</div> <div class="entry-copy">{{content.description}}</div> </div> </div> '; var noteTemplate = ' <div class="entry-note"> <h2> </h2> <div class="entry-text"> <div class="entry-title">{{content.title}}</div> <div class="entry-copy">{{content.data}}</div> </div> </div> '; <pre><code>var getTemplate = function(contentType) { var template = ''; switch(contentType) { case 'image': template = imageTemplate; break; case 'video': template = videoTemplate; break; case 'notes': template = noteTemplate; break; } return template; } var linker = function(scope, element, attrs) { scope.rootDirectory = 'images/'; element.html(getTemplate(scope.content.content_type)).show(); $compile(element.contents())(scope); } return { restrict: "E", link: linker, scope: { content:'=' } }; </code></pre> });
Feel free to grab the project and see what you can come up with.
Lukas.Over.and.Out!
Resources
The Source
https://github.com/simpulton/angular-dynamic-templates
$compile Documentation
http://docs.angularjs.org/api/ng.$compile
AngularJS Mailing List
https://groups.google.com/forum/?fromgroups#!forum/angular
Why not just use ng-switch inside of your ng-repeat? Is there a specific reason you chose not to do it that way?
In one of my projects, this is how I am doing it. ( This is a raw paste from the project ! )
IT ate up my html!!!! damn…
Haha… throw up a jsfiddle and we can compare.
Bravo Lukas! Great tut! Thanks for sharing
Thanks to you Lukas, I can now build a great app 😀
Thanks a lot for sharing!
Great post!
I recently did something similar to distinguish between text, photo, and event posts. Instead of creating a new directive, however, I opted for a more simple approach using ng-switch (like Ganaraj mentioned). I’m not sure what the performance difference is, but I prefer ng-switch because it keeps all HTML within the template.
Here’s a jsFiddle implementing the same functionality: http://jsfiddle.net/UhUP5/1/
Nice! This is a totally legitimate approach.
I think the upside to encapsulating it in a directive the way that I did is maintainability and portability. If you had a bunch of content types, your HTML could get REALLY unruly very quickly.
Also, it makes for easier composition of a view from template fragments especially if there were complex business rules around it.
Thanks for the fiddle. It is definitely useful to see it done that way!
Great post!
I understand $compile() better now!
Good to know the two approaches indeed!
Hi! You can use the ‘ng-switch’ directive inside your template HTML, so that the HTML is encapsulated in the widget, but you still don’t have to play around with the $compile service. Additionally, you can make each content type template into a directive of its own, that way you can encapsulate the functionality of each content type.
Another thing concerning your content loading in the controller: I can’t seem to find the documentation for it, but did you know that you don’t need the ‘fetchcontent’ function? Apparently, if a property in your scope is a Promise, AngularJS will update the property with the returned value once the Promise is resolved.
This means you can write code like:
$scope.content = $http.get(url).then(function (response) { return response.data; });
And angular will update the ‘content’ property to contain the returned ‘data’ value.
Ha! A blog post in my blog post! I like it!
Really great info Schmulik. Thanks for sharing!
Thanks for all these angular posts! They’re really helping me learn how to use the framework.
Regarding dynamic templating, what happens if you set the template attribute of the directive to the template returned by getTemplate? Does that not also compile?
rocks!
Great tut!
Helped me sort out an issue I was having with directives. Thanks.
Hi..I’m really noob with angularjs and I can understand your code but I’ve 2 questions…inside the angularjs documentation, shows than the directive can receive a “compile function” function compile(tElement, tAttrs, transclude) { … } …using this would be similar to this example, where you create a link function and inject the compiler??,
2) when is necessary call the show function?…I’ve found other directives examples and never call the show function…
thanks and good luck…I’m waiting new articls 😀
Great article! I am still struggling with dynamic templates and was thinking about a directive approach so this is a great lead-in. Thanks also to your commenters, I must admit, i wasn’t across the ng-switch approach so that was also interesting.
Great Tutorial, please keep them coming.
I have the same question as ‘angel’ above, what’s the purpose of the show function.
Hi Angel and Ali —
show() is actually a jQuery function which you can call on element because it is a jQuery object.
More information can be found here http://api.jquery.com/show/
It is REALLY convenient having jQuery baked into AngularJS like this because it makes tapping into jQuery super easy.
I cover more of this here http://onehungrymind.com/for-the-designers/ Cheers!
Thank-you for a great post, I learned more about directives and $compile.
I used another approach with keeping logic in the controller… I used an ng-include with its src that is bound to a property in the controller.
Data comes in, updates property that defines the view needed in the scope , fires the bindings, changes the view which loads the proper html, then updates the data in the template… works well so far and no need for directive. Not that I don’t like directives, I like them! But just wanted to share another way of going about changing views on the fly.
Totally legit 😀 Thanks for sharing.
Not to be rude, but by combining ng-switch with ng-include you can do the exact same thing without having to inline html in your js. Ng-include takes an expression so you can do something like ng-include=”‘templateId | determineTemplate'”. If you don’t want to use a filter, you could use a scoped method as well. This gives you the same functionality you are trying to achieve here while allowing you to still build templates as normal.
Not rude at all and I agree 🙂 You COULD do that very thing and that would work in most cases. Where this approach would fall apart is if you had a LOT of variations you needed to account for. What would happen if you had 40 different content types you needed to render? That would very quickly become unmanageable and we are back to the jQuery DOM spaghetti problem of yesteryear. Or! what if you needed to dynamically compose a single DOM structure from multiple templates depending on rules set forth by business logic. The DOM would not be an appropriate place to make those decisions.
There are cases where layout is the result of imperative operations and therefore should be extracted out of a declarative context.
Hey, it is an interesting solution. Have you given any thought to the performance impacts with larger data sets? Diving into the directive workflow and it’s inner working, it seems the reason for the split compile and linking function is for caching in the cases of ng-repeat nesting..
I love your blog. The articles I have read so far are all helpful. Thank you for spending your precious time to guide us through the AngularJS wonderland.
Regarding the use of
.show()
in your linking function, I think Ali and Angel were saying that it was unnecessary.element
was never hidden in the first place, so it was supposed to show by default. I tried taking out.show()
and the app was still running fine.Great article !!!
I have been trying to do a similar thing where I generated input controls for forms. While the view is being rendered dynamically based on the scope input type, form validation seem to have some issues.
I have created a small sample @ http://plnkr.co/edit/R3NTJK?p=preview
I would really appreciate if some one could throw some insight on what exactly the issue is.
Thank you, your tutorials are golden, and written very nicely for an Angular newcomer like me.
I wondered if an adapted version of this method is what I’m after, any help would be greatly appreciated.
I’d making a site with lots of questions. The plan is to write the questions as individual html files named “q1.html”, “q2.html” etc.
So, is it possible to use the method here, but with existing html files (as with partials)?
… and, if it is possible, is it a good idea or should I be working in a different way?
(I initially tried using templateUrl in the directive, but couldn’t get it to work dynamically.)
Cheers
Chris
Chris – thanks for the kind words.
In your case, I would take an entirely different approach. I would mark up all your questions as a JSON data structure and then write a single template to render the questions.
For instance, have an array called $scope.questions and another property called $scope.currentQuestion and bind to that object ie {{ currentQuestion.question }} etc. From there you just have to write your functions to iterate the $scope.questions array such as $scope.nextQuestion and $scope.previousQuestion.
Unless the layout for each question is varying wildly then doing a file for each question is way more work than is necessary. 😀
Thank you, that sounds like a good idea. I’ve seen a few sample questions now and the layout differences should be manageable with your approach.
Thanks
Chris
Did you guys test the same in IE, atleast for me it doesn’t work. Is it because of the Element directive is used?
is it possible to define the templates in separate html files instead of string?
Thanks a lot. I landed on this post by chance and found some good explanations how to implement its own directives. I put into practice and it worked great! 🙂
I am following your example, but seems unclear to me is how do you bind two elements together? For instance if my content type are 2 select inputs and the selection in the first input changes thr options in the second select input, the how would I do this?
I have a problem. The problem is identical to that described, but I have to appear about 10 000 entries. from the fact that at each iteration call $compile script runs very slowly. Any ideas how to speed up?
hey, thanks for sharing this great article!
One problem I’ve found with this approach is that you can’t select your $compiled elements from the DOM after they are compiled, which is troublesome.
So if after your $compile(element.contents())(scope); statement you try console.log($(‘.entry-photo’).html()); … you get null.
I have no idea why ^^
Yup! Custom directives open up a whole world of possibilities. I’ve been replacing bucket loads of javascript code with directive calls in some legacy apps I’m working on. Been using AngularStrap and considering Bootstrap UI. Emberists just don’t get this at all!
Thanks for the article. It really helped.
Hello, and thank you for this blog post 🙂
This describes exactly what i’m trying to do except for one important details I have all my templates in separated files. So I’m really interested by the answer you could give to “@gembin: is it possible to define the templates in separate html files instead of string?”
@gambin and @meewii To accomplish this in separate files you would need to use either the text plugin with RequireJS https://github.com/requirejs/text and inject your template as a string. I have used this technique on a large project and it was very effective. Or! You can also use Grunt as well via https://npmjs.org/package/grunt-angular-templates which I am using on another large project and it works just as well. Hope this helps!
.show() is not just unnecessary, but also breaks the logic
What do you mean ‘breaks the logic’?
Thank you! They should add this as an example directive on the angular site…
Hey
If i need a comment functionality as well then where should i define the function to post comment as the scope of this directive is different from the controller scope.
Thank you for this interesting and very useful article.
Hi, great articlet really helped me alot. Just came to know about $compile thanks for the clear explanation.
first of all i must say really great article! i have a question that maybe you can answer to, i want to put a ng-model into the dynamic template, if i need for example to dinamically create a textarea, how can i bind the content of it starting from this project?
Awesome, the angular equivalent of the WPF data template selector. Love your writing style. Your enthusiasm is a real pick-me-up.
Great post!!! Im using the magic now!! Thank you so much
Hello Simpulton,
I really like the abstraction here. I saw your “Circus Tricks with Angular.js” talk during the JS Summit and thought it was really well done.
I was wondering how you could incorporate dynamically setting ng-model in your template? Using SO I was able to find that this works: ng-model=”newBindingObj[content.Id]” (I extended your JSON a bit). The trouble I am having is determining where newBindingObj exists in the overall scope?
Does this make sense? Can you help out with my dilemma?
Thanks again.
I can’t praise you enough simpulton. This technique is invaluable when it comes to optimizing directives by forcing their respective templates into calculating components that we know in advance that will be immutable once they are weaved into the template. One can use this to eliminate one way data binding all together for templates that call for it.
As of angular 1.3.0-beta10 there is another way to achieve the above: We can use the evaluate-once operator ‘::’ described here:
https://docs.angularjs.org/guide/expression#-event-
Great Article!!. Helped me solve a problem i was facings. Thanks
But now you have markup in your JS.
Isn’t that something we should have already fixed?
In the broad general sense, I believe declarative markup and imperative logic should be separated. But! in this case, we are approximating what AngularJS is doing closer to the metal for power and glory.
Also, it is worth mentioning that you could write your templates in HTML and then inject them as strings via RequireJS and eliminate this ‘problem’.
Also, it is also worth mentioning that I will sometimes use this technique to compile a directive a runtime so I can test it. See egghead.io for an example.
Hi, excellent work. Really amazed on how elegant and simple you made your solution to what seems like a complex problem.
I have a question which I would be grateful if you can help me out on.
If after rendering the templates a new template has been added (so instead of getting content.json from the server, the templates are also retrieved), is there a way to tear down all the DOM elements and rebuild it. So say the first time I show all the data/DOM tree as you have shown and then a new template with its json object need to be loaded. Is there a way of doing the linking/compiling after the DOM tree has been rendered the first time.
Hope this makes sense.
Thanks.
In this scenario, the templates are simply strings and this is where this example shines. You ‘tear down’ the DOM via jQuery and then attach your template and from there compile. You can get your templates from anywhere i.e. the server, a function that composes the templates on the fly, etc and then attach it via jQuery and compile.
First of all: THANKS!
Then, do you think it would be feasible to store the templates in separate files and retrieve only the one we need? Meaning that in for example for the variable imageTemplate we store a string that is the templateUrl of that template, instead of the template itself. Would it slow things down?
Finally: is it normal that I can’t see the other comments?
Hi Leonard — you certainly can although I recommend using something like html2js https://github.com/karlgoldstein/grunt-html2js to process all your templates into a single file. RequireJS also allows you to inject templates as strings as well. Worked like a champ!
replace does not work
Removed. Thanks for pointing that out! #highFive
This was a great resource – it helped me to find a bug in our code that was preventing AngularJS from attaching to the template. We had:
$compile( element.html() ) (scope);
but should have had:
$compile( element.contents() ) (scope);
Nice!
Hey, Great tuto. Ty
Great, exactly what is was looking for! Thanks 🙂
Bravo! Good stuff!
i think this code is most powerfull if templates are loaded dynamically. For example, each entity load your own html from server..
Hi
very useful tutorial.
Thanks allot.
I was wandering if there is a way to load the template from an external file?
Thanks again
Avi
Hi Avi –
Sure… the templates are just strings and so you could load them in a JSON file or even a remote call to a server. You would just need to wrap them in a JSON object and them modify the getTemplate method to return the correct string. // I have added this to the queue for a future blog post.
How do I make the templates come from separate files such as: template-a.html vs template-b.html?
Blog post coming soon… 😀
Thanks heaps for this; just the ticket!
Hey, great tutorial. I’ve used this approach to dynamically create input fields based on the datatype. But I’m stuck with the testing (karma). Could you post some tips about unit testing this directive??
Thanks in advance!!
Hi,
When I downloaded the source code and tried to run that. I am able to see the background image only. I am not able to see the images and the video and notes.
I am new to angular.js. Please help me. Is there any changes to done while running on local machine.
Thanks.
Hi Mahavir — I recommend using the npm module serve to load your webpages. You can read about it here https://www.npmjs.com/package/serve. I also use WebStorm which gives me an HTTP server for free as well when you preview in browser from the IDE.
This solution is a fantastic example of SoC. One note though, this will hit some pretty significant performance issues when you start using complex DOMs with a lot of elements. Please see http://stackoverflow.com/a/22646392/941187 for a fix.
This gist is that you can pre-compile the individual templates and then only link in the scope during the selector’s link phase. This saved my some serious rendering time.
Hi,
Great work, helped me a lot.
I got an issue trying to connect UI-soratble to this dynamic list.
it seems to not be working in any way.
any chance you can assist here?
Could you put it in an plunk?
Here’s a super easy article on ‘Dynamically Compile and Add a custom directive in AngularJs’ with snapshots. http://www.learn-angularjs-apps-projects.com/AngularJs/dynamically-add-directives-in-angularjs
This is a very good article about creating controls dynamically.
I also tried something similar but without a directive. It displays the controls as per the criteria. Below is the code: can something similar be implemented using the directive approach. I’m unable to do so.
Also, I’m unaware if this thing that I’m implementing is efficient or whether a directive approach is better.
var tempJSONObj = [
{ controlname: ‘dropdowncontrol’ },
{ controlname: ‘radiobuttoncontrol’ },
{ controlname: ‘checkboxcontrol’ },
{ controlname: ‘textboxcontrol’ }
];
var testApp = angular.module(‘testApp’, []);
var controllers = {};
controllers.TestController = function ($scope) {
$scope.controls = tempJSONObj;
$scope.getTemplate = function (controlName) {
var template = ”;
var dropdownTemplate = ‘partials/dropdowncontrol.html’;
var radioButtonTemplate = ‘partials/radiobuttoncontrol.html’;
var checkboxTemplate = ‘partials/checkboxcontrol.html’;
var textboxTemplate = ‘partials/textboxcontrol.html’;
switch (controlName) {
case ‘dropdowncontrol’:
template = dropdownTemplate;
break;
case ‘radiobuttoncontrol’:
template = radioButtonTemplate;
break;
case ‘checkboxcontrol’:
template = checkboxTemplate;
break;
case ‘textboxcontrol’:
template = textboxTemplate;
break;
}
return template;
}
};
testApp.controller(controllers);
Hi Ameet — put this in a plunk and I can take a look.
Sweet!
Not to be rude, but by combining ng-switch with ng-include you can do the exact same thing without having to inline html in your js. Ng-include takes an expression so you can do something like ng-include=”‘templateId | determineTemplate’”. If you don’t want to use a filter, you could use a scoped method as well. This gives you the same functionality you are trying to achieve here while allowing you to still build templates as normal. Click here
@jitendra – this would work for simple layouts but can easily spin out of control. What happens when you have to choose between 50 different templates? Are you going to put 50 switch statements in your DOM? Even worse, what happens if you the criteria for choosing the template is not a simple boolean value but based on multiple conditions? You will execute that logic every single digest cycle. Also, what happens if you need to dynamically compose the template on the fly based on business logic? For instance, use a specific template if the user is logged in or not and another template based on the user’s status and so on. I am not advocating using this approach for everything but it is definitely more extensible and allows you to exert more granular control over your view without suffering a huge performance hit during the digest cycles.
Very nice.
I just merged your idea with this one: http://stackoverflow.com/a/24496287/1247387
Final result:
link: function(scope, element) {
// scope.type can be 'text', 'date', 'select' ...
var template = $templateCache.get('/components/filter/filter-'+ scope.type +'.html');
element.html(template);
$compile(element.contents())(scope);
}
Hey, grateful for this post–not only was I looking for this functionality, but all the comments you’ve shared have taught me the alternates, too. You became a resource for this need! 😀
Thank you!
Thanks! I appreciate that. 😀
This is really helpful, exactly what i needed ! 🙂
The fact that this isn’t built into angular by default is something that is puzzling to me. Why go to all the effort of angular if something as simple as using a strategy pattern is this difficult. This is about 3 lines of code in knockout and I don’t have to trust in all the other angular “magic”
thanks, nice job! But I have a problem, I am using templates as Strings, and then these placeholders in templates like {{content.data}}, they don’t get the value, they will be printed as they are like pure texts. No idea why!
Hi Shilan — can you put your code in a plunk so that I can take a look at it? Thanks!
thanks for your quick reply 🙂
surprisingly I removed show() from element.html(getTemplate(scope.content.content_type)).show()
and now it works!
I still don’t understand how!
Sorry for asking too much, but now I have another problem, the ng-change directives that I have in templates, they stopped working when I load templates like this! ng-model is working fine but not ng-change and ng-click.
Hi Shilan — can you put your code in a plunk so I can take a look?
Great tutorial, thanks!
Hi again,
Here is a plunker of what I have implemented.
http://plnkr.co/edit/6eDys3?p=preview
the ng-change won’t work unless you apply $parent.rowHasChanged(rec).
I am not sure that is the correct way of doing that.
besides, i have no idea why my typeahead doesn’t work in the plunker, but typeahead doesn’t even work with $parent.
From my point of view it’s NOT DYNAMIC!!!
In
“item” (content type) is written statically on the place. So you should copy-paste content-item tag everywhere where you want to use it and you should put there correct value of “content” manually. Put cases when business logic says what exact type to use as “content” value, then you can’t do that when you have a lot (1000, 10000000, …) of possible content types.
How is it not dynamic? We are loading templates into the directive DYNAMICALLY based on the content type of the the content object.
I have expanded on this idea in this post AngularJS Dynamic Templates – Yes We Can! Remote Template Edition by loading the templates remoting. You could even load them via a database and that is how you would handle a large amount of content variations.
Just what I needed!
awesome, great. quick fix, needed that. thanks for sharing.
Thanks a lot
Great! I have looking for it serval days!!!!
great post dude!
Thanks! #highFive
Thank you so much for this post! It is great.
I have everything working but I’m trying to modify it so my data-content can contain html(,,, etc.). We are taking content of a variety of types (text, image, video, form inputs) and some are constructed using a rich text editor. Right now when I put html tags in my json object, they just appear as strings after being injected into the template. Any ideas?
If you are trying to render HTML, make sure you are using $sce.renderHtml.
Thank you! Using ng-bind-html and $sce.trustAsHtml(val) worked for me.
Sorry one more question!
I’m using templates for different data acquisition items (text box, image upload, data table) etc. Once people fill them out, I want to send my populated objects back up to the server. Unfortunately, the values I passed into the template do not seem to be binding. The scope isn’t working right. How do I link the scope so the objects I pass into my custom directive can be retrieved after they are edited?
I currently have my directive scope set up the same as the above example.
Hi Holly — can you put your example in a plunk so I can see it? Thanks!
Plunk: http://plnkr.co/edit/T4LePXuoZSCGyI8zmckj
I included a few content types in there so you can see the idea of what I’m trying to do. At the bottom, I’m displaying what is suppose to be the content of the first text input but as you can see by typing into the input, it is not bound to the scope of the controller. This makes sense when I read about isolate scopes. I’m just not sure how to modify it so they will share a scope. I’m trying to get the data people enter in the form using angular’s data binding. Thanks so much for the help!
Just figured it out 🙂 I needed to switch ng-bind to ng-model for 2-way binding for inputs. That’s what I get for being a newb! Thanks for the help.
And what if my current HTML element has some content which I want to be preserved? Think of Angular transclude. With your approach, the old contents will be lost. I guess, using Angular’s
compile()
stage during directive creation would be more appropriate.Hi Martin — I believe in this case you would need to use this in the pre-compile phase of your directive. I have personally never needed to use this approach and would be curious to see use case for it. My inclination would be to try to preserve the state for the entire template in a controller and then compile it all at once. If you want to put something into a plunk, I would be curious to see what you are trying to accomplish. Cheers!
Thanks Lukas — this is really neat and thanks for sharing it!
I am trying to use a data scheme URI as a value for :templateUrl so that I can generate the content (not the URI of) my template from JS. I don’t want to write HTML at all – I want to use JAML like syntax for maintainability and so I don’t have multiple syntax-es in files. I want to protect the View/Model integrity of my app, of course.
I don’t think NG support the data scheme though. Anyone thought about this? Can I get the template content read from shared memory instead of disk?
Thanks!
Hi Derek —
I am having a hard time visualizing your problem. Could you please put it in a plunk and we can geek out on it. Thanks!
Will try to plunk somehting, but my use case is pretty simple on the face of it. Bear in mind I’m really new to NG.
I want to “corrupt” templateUrl in the route object (the 2nd arg to when, after the route pattern thingy). The normal use is to supply an URI as a tring from which the (unexpanded/evaluated) template is read. Typically, people give a relative URI to a static (almost) .html file on disk — the usual template.
I want to generate the content of the (almost) html file template in memory, inside my controller to a data: URI that I give as the templateUrl value.
I suspect that the content of the template needs to be compiled before my controller gets called though, but maybe there is a delay before the content is read? If I give a function as template url with console.log side effect I see that the URI string is read before the controller is invoked, but is the URI content actually read then too? I think so, unfortunately. This feels like a performance necessity or something. Also, the thing is named $compile for goodness sake. Note sure what time windows in NG are available for compilation nor what data would be accessable.
I am looking for a framework where the template is dynamically generated from JS code. I want to use Jaml like JS as syntax rather than HTML so that I don’t have to have stuff like
{{ heyNow }}
that si subject to different syntax b/c with this being dynamically typed, I want typos to glare at me. I also hate editing mixed syntax-es. I /really/ just want s-exps everywhere 😉 Also a pony, natrually, while I’m wishing.
Thanks for the quick reply — Awesome!!
To simplify the concept I am covering here… we are just taking an HTML template string and hooking it into Angular so that we get the benefit of binding. Our HTML template string can be constructed in near infinite variations depending on your use case. I looked at JAML and the difference is that it is specifically designed for a one-off rendering as opposed to having a ‘living’ Angular template after compilation. For that work, you are going to need binding in your templates i.e. what if heyNow represented some state that was going to change in the future? You would want to reflect that state and {{ heyNow }} is that way to do it.
I also wrote about how you could do this asynchronously in this post AngularJS Dynamic Templates – Yes We Can! Remote Template Edition
Yeah, I get that you’re doing here (and it’s very cool!).
I was thinking of trying to do the shared mem trick I sort of described. [1]
Thanks for putting words to something for me: I am indeed interested in generating each page, on the fly, every time — no templates. The JAML stuff was trying to make a point, but was a distraction!
I thought I might be able to use an element mathcing directive and one-element template that gets me to JS really cleanly (but annoyingly), but I really wan to generate the template too. I /do/ want to keep C/V decoupling clean (or course).
I’ll stop the tangent — sorry about that. But thanks a lot for some inpiring ideas and thanks again for shaing this hack and helping people understand it! Right on Lukas — you are winning 😉
[1] I think if I try my hack with ansync IO / callback I’ll get deadlock 🙂 I was thinking about doing it after my last post, but I’m done pursing this corruption.
Hi Lukas, it is a great tutorial and works very well. I have only a SUGGESTION:
If the template being loaded contains a controller, your directive DOES NOT destroys its scope, neither implicitly when invoking element.empty(). YOU NEED to explicitly invoke the $destroy method to that scope token. In order to do that you need to keep track of the child scope inside the template.
I found Ben Nadel’s principle http://www.bennadel.com/blog/2706-always-trigger-the-destroy-event-before-removing-elements-in-angularjs-directives.htm useful in this point.
In order to let the child scope be available to father’s you should IMPLEMENT A SERVICE containing a shared variable between controllers in templates and the directive. Finally you can insert an instruction invoking $destroy method @row 56 just before the html substitution.
Hope this helps to improve your well done code.
Valid input but this is a bit of a nuanced conversation. For instance, it depends on what you are doing IN the controller that determines the necessity of using the destroy event. It also depends on if you are removing the element or not.
Hi,
Is there a way to decompile?
Does every time you compile the digest tree is getting bigger and bigger. I’m asking dooes questions because I’m building POC for app that you can build pages and component inside of them dynamic with drug and drop to the screen.
That is the nature of the $digest cycle. The more watchers we have the larger the digest tree. The solution is almost overly-simplistic in that we simply need to reduce the amount of watchers we have on the page. Here are a few pointers on how to do this… 1. use one-way data binding where you are not going to need to recalculate the template 2. reduce the expressions in your templates – for instance, instead of binding to ng-if=”A && B || C” calculate that one time and bind to ng-if=”isShowing” or something to that effect 3. do not use built-in filters if performance is an issue but rather manually trigger them 4. keep your objects thin and only keep properties on them that you need to satisfy the view – larger objects make for larger digest cycles 5. make sure you use $onDestroy to clean up after yourself
Hope this helps!
Hi,
This is great article. I want to devise a solution with same design pattern.
1) I have multiple html files containing same form. The difference lies in attributes of those elements such as sometimes they are readonly, sometimes some elements aren’t used (can be hidden). I want to make this form as a template & do ng-include & dynamically load template according to element attribute needs. Is it possible with your method?
2) Is applying your method will have performance overhead over using separate html files as I have implemented now, considering separate html files will be limited?
Thanks,
Harshad
Hi Harshad —
I would probably not use ng-include to dynamically populate a form unless it was a REALLY simple form which does not sound like this is what you are shooting for. The problem with ng-include is that depending on how you package your application, you will incur an HTTP request for each template which is one of the WORST things you can do for performance especially on a mobile device.
What I recommend is dynamically compiling your entire form into a single HTML string based on the model and compiling it one time.
Thanks! That’s what’s exactly the directive I was searching for, I’m not sure but I think it’s lighter that the ng-show/ng-hide way, at least we don’t have useless DOM elements.