Reputation: 11
Essentially, I take two templates inside a compile function of a directive. I compile them once and for all (via $compile) in the compile phase of the directive. In the link phase of the directive, I watch a scope variable and applies one or the other compiled template to the scope.
testApp.directive('testField', ['$compile', function ($compile) {
return {
restrict: 'E',
scope: true,
compile: function(tElement, tAttrs) {
var viewFn = $compile("<div>View: <span ng-repeat='x in [1,2,3]'>{{x}}</span></div>");
var editFn = $compile("<div>Edit: <span ng-repeat='x in [4,5,6]'>{{x}}</span></div>");
return function (scope, element, attrs) {
var innerScope = null;
scope.$watch("mode", function (mode) {
if (innerScope) innerScope.$destroy();
innerScope = scope.$new();
if (mode == 'VIEW') {
element.empty().append(viewFn(innerScope));
} else {
element.empty().append(editFn(innerScope));
}
});
};
}
};
}]);
It works fine, except when the template includes a Ng-repeat that is not the root element in which case it behave strangely:
To reproduce, go to http://plnkr.co/edit/RCqlNWTVdXVoMQCkFcQn?p=preview And switch a couple of time from Edit to View.
You'll notice the number of iterations of ng-repeat grows over time. First time, it displays 123 and 456, like it should
After the first back and forth between view and edit, it displays 123123 and 456456
And it keeps adding one iteration every time you do a back and forth between view and edit.
Upvotes: 0
Views: 2552
Reputation: 1920
Maybe you can do like this.
Firstly, you inject new dom directly in your view. After, catch it and apply $compile.
like this;
var viewFn = "<div>View: <span ng-repeat='x in [1,2,3]'>{{x}}</span></div>";
...
element.html(viewFn);
...
$compile(tElement.contents())(innerScope);
Upvotes: 1
Reputation: 11
I probably found the solution shortly after posting.
The problem apparently lies in the fact that ng-repeat needs to clone the template.
After stumbling on a comment in the angular source, turns out we can pass a second argument to the function returned by compile.
This second argument is a function that is called with a clone of the template (and the scope being linked to).
so instead of
if (mode == 'VIEW') {
element.empty().append(viewFn(innerScope));
} else {
element.empty().append(editFn(innerScope));
}
I can do
function setElts(elts) {
element.empty().append(elts);
}
if (mode == 'VIEW') {
viewFn(innerScope, setElts);
} else {
editFn(innerScope, setElts);
}
Upvotes: 1