-
Notifications
You must be signed in to change notification settings - Fork 27.4k
Dynamic templateUrl in directives #1039
Comments
not quite what you want but this will work:
the problem here is that the template is not compiled. to compile it you'd have to inject $compile service, compile the fetched html template. To link the compiled template with a scope, you'd have to return a linking fn from the compile fn that will keep a reference to the scope. Yeah, it's a lot of work, but it's doable. :-) we'll consider this RFE post 1.0 |
+1. many people is asking this feature so what are the main reasons to not implement it? |
+1 |
1 similar comment
+1 |
Did someone completed solution @IgorMinar suggested? I have similar problem: I have a directive, which content depends on some data, that should be loaded from resource (full problem is: I what validations/presentations of model elements loaded from backend. So I'll have kind of and type of field is loaded from backend, and only after it is loaded I can decide which template to compile... For not the only way how to achieve that is try to implement idea, mentioned at @IgorMinar's comment. |
Hi Coli, maybe my fork help you: https://github.com/josefernandotolentino/angular.js Use can use parameters in route templateUrl:
|
@josefernandotolentino you are talking about different templateUrl - the original issue was opened for directives while you are talking about |
ok pkoz, for this reason I said "maybe". |
Based on @IgorMinar 's comments above, I was able to quickly able to come up with a working solution. Now, all I want to know is whether this is the "right" solution, in terms of efficiency/correctness. Any "gotcha"s that I may have overlooked ? Off-Topic: By the way, I just went from "heard of AngularJS for the first time" to "learning AngularJS" to "single-handedly fully re-wrote a moderately complex mobile application" in 3 weeks time (which took a team of three 2.5 months to do in jQMobile & Mustache) ! That says a lot for just how fantastic AngularJS is. Big fan right here. Here's the code. It should be simple enough to imagine the rest of the (controller and template) code - // activity items can be of several types .. post-photo, status-change, etc. (think facebook activity stream)
App.directive('activityItem', ['$compile', '$http', '$templateCache', function($compile, $http, $templateCache) {
var templateText, templateLoader,
baseURL = 'partial/tpl/',
typeTemplateMapping = {
photo: 'photo-activity-item.html',
status: 'status-activity-item.html'
};
return {
restrict: 'E',
replace: true,
transclude: true,
scope: {
type: '@type', title: '=', authorName: '=', avatar: '=', timeAgo: '=',
statsLikes: '=', statsViews: '=' // some basic stats for your post
},
compile: function(tElement, tAttrs) {
var tplURL = baseURL + typeTemplateMapping[tAttrs.type];
templateLoader = $http.get(tplURL, {cache: $templateCache})
.success(function(html) {
tElement.html(html);
});
return function (scope, element, attrs) {
templateLoader.then(function (templateText) {
element.html($compile(tElement.html())(scope));
});
};
}
};
}]) <!-- simple bootstrap list for, say, all photo posts (hence the hardcoded @type) -->
<ul class="nav nav-list">
<li ng-repeat="post in posts">
<activity-item
type="photo"
title="post.postTitle"
author-name="post.creatorName"
time-ago="post.formattedCreatedTimestamp"
stats-likes="post.likeCount" stats-views="post.viewsCount"
stats-comments="post.threadCount"
avatar="post.creatorAvatarPath" >
</activity-item>
</li>
</ul> |
@etchemkay I use a similar method to re-use a form (so no copy-paste) for editing and creating (CRUD), in this context it is editing and creating a calendar event: app.directive('eventForm', function() {
return {
restrict: 'E',
replace: true,
templateUrl: '_form.html',
scope: {
event: '=' // event attribute is binded to the event object of the directive
}
};
}); Note the use of <!-- _form.html -->
<input type="text" ng-model="event.title">
<textarea ng-model="event.description"></textarea>
[...] <h1>Create new calendar event</h1>
<form>
<event-form event="event"></event-form>
[...]
</form> <h1>Edit existing calendar event</h1>
<form>
<event-form event="event"></event-form>
[...]
</form> For sure the use of <h1>Create new calendar event</h1>
<form>
<ng-include src="'_form.html'"></ng-include>
[...]
</form> Ideally I would prefer to use directives for JavaScript code and re-use HTML code through |
+1 |
3 similar comments
+1 |
+1 |
+1 |
I've proposed an implementation of dynamic templates using functions in #1849. Take a look, may be it fits your needs. |
+1 |
1 similar comment
+1 |
+1 |
Actually, this is now supported in 1.1.4 |
@coli could you share an example? the docs specify it's supported but every function I try or way I try it won't work 😢 Update: Nerver mind, to anyone if this is not working for you please make sure you are using 1.1.4.. (My bad)!! |
@coli how does the implemented solution looks like? |
@powtac in place of template or templateUrl, provide a function :) |
Got it! Thanks! |
And does that function's parameters are resolved by DI? |
@olostan nope, check the angular source code |
+1 |
2 similar comments
+1 |
+1 |
This is also possible for creating dynamic templates:
Now your controller may decide which template to use:
Because you have access to your scope parameters, you could also do:
So your server could create a dynamic template for you. |
I've been fighting with this for awhile, so I wanted to toss in the solution I came up with. I was looking to create a directive that changed an editor based on the selected type. Each type editor is its own directive with an isolated scope. I was having difficulty with the ng-include method (my loaded template ended up with a blank scope), and didn't want to remove the scope isolation from my sub-directives as it's needed elsewhere. There are clearly changes that need to be made (type validation, for example), but this gives a working solution. .directive('typeEditor', ['$compile', function($compile) {
var def = {
restrict: 'E',
scope: {
type: "=",
action: "=",
},
link: function(scope, iElement, iAttrs, controller) {
scope.$watch('type', function(newType) {
if (newType != "" && newType != undefined) {
iElement.html('<' + newType + '-editor action="action"></' + newType + '-editor>');
$compile(iElement.contents())(scope);
}
})
},
};
return def;
}]) This would seem to be easily extensible for any level of dynamic template contents. |
@uh-nmb interesting solution.... what problem could probably be, if type directive binds some event listeners, those binding could be not unbound when you change type. I think its better to do something like:
|
+1 |
https://groups.google.com/forum/#!topic/angular/-tYAplxox68 contains a great workaround using ng-include. |
Hmmm, people, this is already supported in the latest version of AngularJS. Unsubscribing myself.... |
@coli As far as I know there is still no way to bind the templateUrl to scope. Which is probably what a lot of people (including myself) were +ing it for. |
Open a new issue please :) this one is long closed. |
As a last note, the ideal declarative way that the first poster describes, is certainly possible! Only, instead of doing <div {{directiveNameInScope}}></div> You can do <div loaddirective="directiveNameInScope"></div> Using this simple directive here: https://github.com/willemmulder/loaddirective Happy coding! |
+1 |
I am considering using AngularJS and would like to swap its HTTP template fetching for something else:
Is something like this possible? How to provide my own provider for the HTML data. Sorry if it is the wrong place to ask, but it looks closest to what I could find. |
No-one seems to have provided an example of the new way to do this so here is a clock directive I wrote using using an optional "template-url" attribute (uses '/app/partials/clock.html' if no attribute is set). This works in Angular 1.2.4: Directive:
Example usage:
The partial (/app/partials/footer-clock.html):
|
Something that isn't mentioned above is that templateUrl makes an XHR request with Accept headers set to "application / json", this isn't great because it's telling the server that it wants json back when really it wants HTML back. Is there a way to get templateUrl to set Accept headers to "text/html"? |
@unluckypixie the problem is that the original post is wanting to avoid the hardcoded string path for the |
ngInclude implements all $compile functionality itself, I think we can use it inside directive: <my-directive ng-include="element.template" config="{element.config}"></my-directive> template is dynamic and configs come as object. I've used this method for a popup with 4 different templates in a project. |
@TR0L, I'm a newbie trying to learn SPA stuff and you saved me so much trouble with your solution! Thank you so much! |
|
Why are given continuously example of the link:! None of these examples does not work when the templateUrl: is used. The templateUrl: for the templates is a complex and long code is required. |
There is a clean way for this: assuming
|
If we transition to an 'abstract' (read: dynamic) state that can handle multiple different templates/controllers based on some value returned to us by a controllerProvider/templateProvider - we need to make sure we dont use the previous invokation(s) template. As such, we need to check for the existence of `templateProvider` on the original state. If that is a function, we need to make a second request through $templateRequest and manually compile our template. Related issues: angular/angular.js#2095 angular/angular.js#1039
+1 |
I'd like to be able to have templateUrl be configurable
eg
Right now, this can be done via compile()
Problem is this only works for inline template, I'd like to be able to fetch it via network if it is not available yet.
Ideally, the delayed compile function associated with templateUrl processing in Angular should be exposed to directive writers.
Very ideally, I'd like to be able to
The text was updated successfully, but these errors were encountered: