Removing ViewRef causes error in Ivy #38201
Comments
|
Nice one. Looks like the array item removal being done on... // view_engine_compatibility.ts
// ViewContainerRef
remove(index?: number): void {
this.allocateContainerIfNeeded();
const adjustedIdx = this._adjustIndex(index, -1);
removeView(this._lContainer, adjustedIdx);
removeFromArray(this._lContainer[VIEW_REFS]!, adjustedIdx); // <--- this line
}...needs to be occurring... // node_manipulation.ts
export function removeView(lContainer: LContainer, removeIndex: number) {
const detachedView = detachView(lContainer, removeIndex);
// <--- here
detachedView && destroyLView(detachedView[TVIEW], detachedView);
}As a workaround, you can just call export class DropdownDirective implements OnInit, OnDestroy {
// ...
ngOnDestroy() {
// this.host.remove(this.ref);
this.ref.destroy();
}
} |
|
Thanks, @Achilles1515 don't I need to remove it though? What happens to the destroyed but not removed view? Will that also work for ComponentRefs hostView? I checked it logging length of ViewContainerRef and it seems to be a valid workaround. |
|
@waterplea this looks like a valid bug in ivy indeed! I wonder if you would like to send a PR correcting this issue, given that you did quite a bit of digging into it? I can help with the process getting PR to the "land-able" state. If not I can fix this issue, please let me know what works best for you. |
Calling See here destroy(): void {
if (this._appRef) {
this._appRef.detachView(this);
} else if (this._viewContainerRef) {
const index = this._viewContainerRef.indexOf(this);
if (index > -1) {
this._viewContainerRef.detach(index);
}
this._viewContainerRef = null;
}
destroyLView(this._lView[TVIEW], this._lView);
}It should work for |
|
One route would be to just remove the angular/packages/core/src/render3/node_manipulation.ts Lines 358 to 361 in 0879d2e Doing so would make the remove(index?: number): void {
this.allocateContainerIfNeeded();
const adjustedIdx = this._adjustIndex(index, -1);
const detachedView = detachView(this._lContainer, adjustedIdx);
if (detachedView) {
// Before destroying the view, remove it from the container's array of `ViewRef`s.
// This ensures the view container length is updated before calling
// `destroyLView`, which could recursively call view container methods that
// rely on an accurate container length.
// (e.g. a method on this view container being called by a child directive's OnDestroy lifecycle hook)
removeFromArray(this._lContainer[VIEW_REFS]!, adjustedIdx);
destroyLView(detachedView[TVIEW], detachedView);
}
}As referenced in my first comment, another option is to move the removeFromArray(this._lContainer[VIEW_REFS]!, adjustedIdx);call into the |
|
I think removing function should be fine. I'll look into it more later, @pkozlowski-opensource I will try to make a PR. |
1�738317) When removal of one view causes removal of another one from the same ViewContainerRef it triggers an error with views length calculation. This commit fixes this bug by removing a view from the list of available views before invoking actual view removal (which might be recursive and relies on the length of the list of available views). Fixes #38201. PR Close #38317
…ngular#38317) When removal of one view causes removal of another one from the same ViewContainerRef it triggers an error with views length calculation. This commit fixes this bug by removing a view from the list of available views before invoking actual view removal (which might be recursive and relies on the length of the list of available views). Fixes angular#38201. PR Close angular#38317
|
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Affected Package
The issue is caused by package @angular/coreIs this a regression?
Yes, the previous version in which this bug was not present was: 8Description
I have a dropdown directive that creates a new view in a
ViewContainerRefof dropdowns host component. If that new view also has this directive it can create another view. InngOnDestroyof that directive I remove this view fromViewContainerRef.When parent dropdown is removed, I get an error in Ivy on destruction of nested directive created view.
Seems like destruction of first view causes destruction of the second view in sync and when I attempt to remove second view I get error, because length is already updated as if parent view is removed (
lengthis 1):angular/packages/core/src/render3/view_engine_compatibility.ts
Lines 209 to 211 in 0879d2e
However both views are still there in
indexOf:angular/packages/core/src/render3/view_engine_compatibility.ts
Lines 299 to 302 in 0879d2e
So nested view reports being at index of 1 and this assertion fails thinking there is only index of 0 because it thinks
lengthis 1:angular/packages/core/src/render3/view_engine_compatibility.ts
Lines 321 to 331 in 0879d2e
Toggle parent, then toggle child, then toggle parent again to have them both removed
https://stackblitz.com/edit/angular-ivy-viewref-bug
Here's working example on Angular 8 with ViewEngine:
https://stackblitz.com/edit/angular-ve-viewref
Angular Version:
The text was updated successfully, but these errors were encountered: