Angular.js directive dynamic templateURL
Solution 1
You can use ng-include
directive.
Try something like this:
emanuel.directive('hymn', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
scope.getContentUrl = function() {
return 'content/excerpts/hymn-' + attrs.ver + '.html';
}
},
template: '<div ng-include="getContentUrl()"></div>'
}
});
UPD. for watching ver
attribute
emanuel.directive('hymn', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
scope.contentUrl = 'content/excerpts/hymn-' + attrs.ver + '.html';
attrs.$observe("ver",function(v){
scope.contentUrl = 'content/excerpts/hymn-' + v + '.html';
});
},
template: '<div ng-include="contentUrl"></div>'
}
});
Solution 2
emanuel.directive('hymn', function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
// some ode
},
templateUrl: function(elem,attrs) {
return attrs.templateUrl || 'some/path/default.html'
}
}
});
So you can provide templateUrl via markup
<hymn template-url="contentUrl"><hymn>
Now you just take a care that property contentUrl populates with dynamically generated path.
Solution 3
Thanks to @pgregory, I could resolve my problem using this directive for inline editing
.directive("superEdit", function($compile){
return{
link: function(scope, element, attrs){
var colName = attrs["superEdit"];
alert(colName);
scope.getContentUrl = function() {
if (colName == 'Something') {
return 'app/correction/templates/lov-edit.html';
}else {
return 'app/correction/templates/simple-edit.html';
}
}
var template = '<div ng-include="getContentUrl()"></div>';
var linkFn = $compile(template);
var content = linkFn(scope);
element.append(content);
}
}
})
Solution 4
You don't need custom directive here. Just use ng-include src attribute. It's compiled so you can put code inside. See plunker with solution for your issue.
<div ng-repeat="week in [1,2]">
<div ng-repeat="day in ['monday', 'tuesday']">
<ng-include src="'content/before-'+ week + '-' + day + '.html'"></ng-include>
</div>
</div>
Solution 5
I had the same problem and I solved in a slightly different way from the others. I am using angular 1.4.4.
In my case, I have a shell template that creates a CSS Bootstrap panel:
<div class="class-container panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">{{title}} </h3>
</div>
<div class="panel-body">
<sp-panel-body panelbodytpl="{{panelbodytpl}}"></sp-panel-body>
</div>
</div>
I want to include panel body templates depending on the route.
angular.module('MyApp')
.directive('spPanelBody', ['$compile', function($compile){
return {
restrict : 'E',
scope : true,
link: function (scope, element, attrs) {
scope.data = angular.fromJson(scope.data);
element.append($compile('<ng-include src="\'' + scope.panelbodytpl + '\'"></ng-include>')(scope));
}
}
}]);
I then have the following template included when the route is #/students
:
<div class="students-wrapper">
<div ng-controller="StudentsIndexController as studentCtrl" class="row">
<div ng-repeat="student in studentCtrl.students" class="col-sm-6 col-md-4 col-lg-3">
<sp-panel
title="{{student.firstName}} {{student.middleName}} {{student.lastName}}"
panelbodytpl="{{'/student/panel-body.html'}}"
data="{{student}}"
></sp-panel>
</div>
</div>
</div>
The panel-body.html template as follows:
Date of Birth: {{data.dob * 1000 | date : 'dd MMM yyyy'}}
Sample data in the case someone wants to have a go:
var student = {
'id' : 1,
'firstName' : 'John',
'middleName' : '',
'lastName' : 'Smith',
'dob' : 1130799600,
'current-class' : 5
}
Related videos on Youtube
Alen Giliana
Updated on July 13, 2020Comments
-
Alen Giliana almost 4 years
I have a custom tag in a
routeProvider
template that that calls for adirective
template. Theversion
attribute will be populated by the scope which then calls for the right template.<hymn ver="before-{{ week }}-{{ day }}"></hymn>
There are multiple versions of the hymn based on what week and day it is. I was anticipating to use the directive to populate the correct
.html
portion. The variable is not being read by thetemplateUrl
.emanuel.directive('hymn', function() { var contentUrl; return { restrict: 'E', link: function(scope, element, attrs) { // concatenating the directory to the ver attr to select the correct excerpt for the day contentUrl = 'content/excerpts/hymn-' + attrs.ver + '.html'; }, // passing in contentUrl variable templateUrl: contentUrl } });
There are multiple files in excerpts directory that are labeled
before-1-monday.html
,before-2-tuesday.html
, …-
Nick Grealy about 9 yearspossible duplicate of Dynamic templateUrl - AngularJS
-
hkong about 7 yearsif you're using AngularJS 1.5+, check this elegant solution: stackoverflow.com/a/41743424/1274852
-
-
Alen Giliana over 10 yearsIts great solution. Is there a way to write it that it can handle multiple instances? Currently, once the scope is set it does not recognize new attrs.ver.
-
pgregory over 10 yearsYou mean, you want to watch
ver
attribute changes and rerender directive? -
Alen Giliana over 10 yearsHey @pgregory, I will have more than one <hymn> tag in a template and I was planning to have the directive render them based on the ver tag.
<hymn ver="before-1-monday"></hymn> ... <hymn ver="after-1-monday"></hymn> ... <hymn ver="seasonal-1-monday"></hymn>
-
pgregory over 10 yearsThank you for clarifying. If you declare directive the way posted in upd., your use case when you using multiple
<hymn ...>
should work well. Or maybe its time to construct a prototype at jsfilddle? -
Alen Giliana over 10 yearsI tried to prototype into JSFiddle but I couldn't get it to link directives to external pages URL. At least you will see how it is formatted. sample page on my site
-
pgregory over 10 yearsHello @AlenGiliana, I
ve take a look at your site, and changed [JSFiddle](http://jsfiddle.net/JQgG5/6/). All you need is
scope:{}` in directive declaration - scope isolation. Also I strongly recommend you to use last version of angular.<script type="text/ng-template" id="...">
- is local alternative to html pages -
Alen Giliana over 10 yearsDo you mean to use Angular 1.2.1? Thank you for the help by the way, this learning curve is insane :)
-
pgregory over 10 yearsYes, using last stable versions of 3rd party libraries is good practice) At the moment angularjs 1.2.13 is last
-
josec89 about 10 yearsNice, but... can I access to scope attributes from the templateUrl function? The templateUrl depends on a scope value, but I can't access to it :(
-
Andrej Kaurin about 10 yearsNope. Maybe you should explain exactly what you need so we can find solution. It is probably that you can send everything you need via attrs.
-
josec89 about 10 yearsI found the solution! :) I've created a directive called 'entry'. An entry has a 'type' (int, string, date...) and, depending on the type of the entry, you have one template or another (an input type='text', input type='number', some plugin for date....). The thing is that I pass the entry type through the scope and I couldn't use it to decide which template should be loaded. I used the method written in the other answer. I had problems accessing to the scope with ng-include (because it creates a new scope), but I solved it useing $parent. Thanks btw :)
-
Andrej Kaurin about 10 yearsI am glad you found solution. I would NOT recommend directive having dependency on its parent unless it is controller set in require part of directive.
-
cfd_aero about 10 yearsFinally! Exactly what I was looking for! I didn't realize I had access to elem and attrs from a templateUrl function. THANKS!
-
Are Almaas almost 10 yearsAwesome. Would change the ng-include to a scope variable instead of a function. If it's a function it repeats alot of times. That's what happens to me atleast..
-
Lu4 almost 9 yearstemplateUrl is called once per directive, it is not being called upon each directive instance initialization, be careful!!! It may be bug in angular though...
-
Ian Vaughan almost 9 years@Lu4 I have to disagree with that, as I am using the directive twice in a template with a different attribute value passed in, and it renders the different template as per the attribute correctly.
-
Lu4 almost 9 yearsI haven't checked it yet but according to my latest findings, It's probably worth mentioning that it's
once per $compile phase
. In other words if you useng-repeat
with your directive and want to set individual template based on specificng-repeat
item context, it won't work, because$compile
phase walks through your directive once before actualng-repeat
happens. So in that meaning it's being called once... -
Andi Giga almost 9 yearsWith this solution my double bindings don't work anymore. The solution of Andrej Kaurin works for me.
-
Ashraf Sabry over 5 yearsI wonder how this answer got all these upvotes? This won't work because the
attrs
object contains the attribute values as read from the DOM objects i.e., string values. The OP wanted to evaluate expressions against the scope. In your example,attrs.templateUrl
value will be just the constant string'contentUrl'
. See this plunk next.plnkr.co/edit/N1FHOvaH7I5DVnmO