Reputation: 14734
I have a need for a <select>
on a form to control the remaining available inputs. This is achieved easily with angular by binding the <select>
to the src of an ng-include
.
The problem is each time the user changes the selected value (and hence the src
template), angular compiles the template from scratch and attaches a new scope. This has the effect of wiping all the input data each time. The user expects if they change back to a previously selected option, all the field data they previously entered will still be there.
Is there any built in way to achieve the desired behaviour? Ideally an option so that the ng-include
will re-use a previously compiled template and associated scope?
One workaround is to ng-include
all the templates, and use ng-show
to only display the current one. This seems heavy though, there is a lot of DOM elements on the page unnecessarily. (Especially for my use-case where there will be about ~20 different templates, and this whole <select>
element and dynamic template control may be repeated on a single page up to 40 times.)
Here is a jsFiddle. The desired outcome is the counter on template 1 is persisted when changing the dropdown to template 2 and back to 1.
Upvotes: 1
Views: 433
Reputation: 11190
I found this question interesting. Short of putting the model in a service (which is probably the correct way), I don't think there is a built in way to cache templates with ng-include without using ng-repeat, as demonstrated in this fiddle.
<div ng-controller="Ctrl">
<select ng-model="template" ng-options="t.name for t in templates">
<option value="">(blank)</option>
</select>
url of the template: <tt>{{template.url}}</tt>
<hr/>
<div ng-repeat="t in templates" ng-include="t.url" ng-show="template.name == t.name">
</div>
</div>
As you point out, the downside of this solution is you end up with a lot of elements in the DOM.
For fun, I wrote a version of ng-include that caches the template element and reuses it. You still end up potentially creating just as many DOM nodes in the worst case, but since they are created only on-demand, and since only one is ever in the DOM at any given time, it should remain fairly efficient from an angular perspective.
Here is the fiddle for this directive.
.directive('cacheInclude', function ($compile, $http, $templateCache) {
return {
link: function (scope, element, attrs, ctrl) {
var cache = {};
var currentElement = element;
var replaceCurrent = function(cacheEntry) {
currentElement.replaceWith(cacheEntry);
currentElement = cacheEntry;
};
scope.$watch(function(){
return scope.$eval(attrs.src);
}, function () {
var src = scope.$eval(attrs.src);
if (!cache[src]) {
$http.get(src, {cache: $templateCache}).then(function (result) {
cache[src] = $compile(result.data.trim())(scope.$new());
replaceCurrent(cache[src]);
});
} else {
replaceCurrent(cache[src]);
}
});
}
}
})
It's not "built in", but I think it is a good middle ground solution. Note, the directive is "for example only" and still requires error handling, etc.
Upvotes: 1