weird behaviour of angularjs directive when wrapped
-
21-12-2019 - |
Question
I have a directive like this, which wraps element with a container.
app.directive('myDirective', function($compile, $timeout) {
var num=0;
return {
link: function(scope, el, attrs) {
var uniqNum = scope.$index || num++;
var container = document.createElement('div');
container.className = "container";
el.wrap(container);
}
};
});
<textarea my-directive ng-repeat="str in arr track by $index" ng-model="arr[$index]">
</textarea>
However, as you see in demo, http://plnkr.co/edit/wOulzF?p=preview, whenever you start typing into the textarea, the element go outside of the container.
If ng-model is not an arr, it works fine.
Anyone can explain this behaviour?
La solution
Your directive is running before ngRepeat
. When Angular compiles an ngRepeat
it places special comments in the code that it uses as markers. Since your directive is running before those are added to the DOM your wrapper doesn't enclose those important comments. Instead the html that is created initially looks like this:
<!-- ngRepeat: str in arr track by $index -->
<div class="container">
<textarea my-directive="" ng-repeat="str in arr track by $index" ng-model="arr[$index]" class="ng-scope ng-pristine ng-valid"> </textarea>
</div>
<!-- end ngRepeat: str in arr track by $index -->
When arr
is changed then ngRepeat
updates the DOM placing the new/updated ngRepeat
element where Angular's marker (the comment) is- which is outside your wrapper. So, after arr
is changed (and a $digest occurs) your html looks like this:
<!-- ngRepeat: str in arr track by $index -->
<textarea my-directive="" ng-repeat="str in arr track by $index" ng-model="arr[$index]" class="ng-scope ng-pristine ng-valid"> </textarea>
<div class="container"> </div>
<!-- end ngRepeat: str in arr track by $index -->
To fix this set your directive priority to 1001 or more (ngRepeat has a priority of 1000):
return {
priority: 1001,
link: function(scope, el, attrs) {.. }
}
Now ngRepeat
will manipulate the DOM first so when your directive wraps the element the ngRepeat
comments will be wrapped too resulting in the following html:
<div class="container">
<!-- ngRepeat: str in arr track by $index -->
<textarea my-directive="" ng-repeat="str in arr track by $index" ng-model="arr[$index]" class="ng-scope ng-pristine ng-valid"> </textarea>
<!-- end ngRepeat: str in arr track by $index -->
</div>
And fixing your problem - updated plunker
Autres conseils
You use the same model for your textarea
as for your ng-repeat
. That means if you change the contents of your textarea
you simultaneously change the model of the ng-repeat
directive. That in turn causes ng-repeat
to get busy again.