fix(ivy): remove DOM nodes from their real parent vs saved parent (#28455)
Currently, DOM node removal called `removeChild` on the saved parent node when destroying a component. However, this will fail if the component has been manually moved in the DOM. This change makes the removal always use the node's real `parentNode` and ignore the provided `parent`. PR Close #28455
This commit is contained in:

committed by
Matias Niemelä

parent
5a2c3ff8b5
commit
89eac702b5
@ -19,7 +19,7 @@ import {RComment, RElement} from './interfaces/renderer';
|
||||
import {SanitizerFn} from './interfaces/sanitization';
|
||||
import {StylingContext} from './interfaces/styling';
|
||||
import {BINDING_INDEX, HEADER_OFFSET, HOST_NODE, LView, RENDERER, TVIEW, TView} from './interfaces/view';
|
||||
import {appendChild, createTextNode, removeChild} from './node_manipulation';
|
||||
import {appendChild, createTextNode, removeNode as removeRNode} from './node_manipulation';
|
||||
import {getIsParent, getLView, getPreviousOrParentTNode, setIsParent, setPreviousOrParentTNode} from './state';
|
||||
import {NO_CHANGE} from './tokens';
|
||||
import {addAllToArray, getNativeByIndex, getNativeByTNode, getTNode, isLContainer, renderStringify} from './util';
|
||||
@ -830,7 +830,7 @@ function removeNode(index: number, viewData: LView) {
|
||||
const removedPhTNode = getTNode(index, viewData);
|
||||
const removedPhRNode = getNativeByIndex(index, viewData);
|
||||
if (removedPhRNode) {
|
||||
removeChild(removedPhTNode, removedPhRNode, viewData);
|
||||
removeRNode(removedPhTNode, removedPhRNode, viewData);
|
||||
}
|
||||
|
||||
removedPhTNode.detached = true;
|
||||
@ -840,7 +840,7 @@ function removeNode(index: number, viewData: LView) {
|
||||
if (isLContainer(slotValue)) {
|
||||
const lContainer = slotValue as LContainer;
|
||||
if (removedPhTNode.type !== TNodeType.Container) {
|
||||
removeChild(removedPhTNode, lContainer[NATIVE], viewData);
|
||||
removeRNode(removedPhTNode, lContainer[NATIVE], viewData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ function executeNodeAction(
|
||||
if (action === WalkTNodeTreeAction.Insert) {
|
||||
nativeInsertBefore(renderer, parent !, node, beforeNode || null);
|
||||
} else if (action === WalkTNodeTreeAction.Detach) {
|
||||
nativeRemoveChild(renderer, parent !, node, isComponent(tNode));
|
||||
nativeRemoveChild(renderer, node, isComponent(tNode));
|
||||
} else if (action === WalkTNodeTreeAction.Destroy) {
|
||||
ngDevMode && ngDevMode.rendererDestroyNode++;
|
||||
(renderer as ProceduralRenderer3).destroyNode !(node);
|
||||
@ -593,13 +593,19 @@ function nativeAppendOrInsertBefore(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a native child node from a given native parent node.
|
||||
*/
|
||||
/** Removes a node from the DOM. */
|
||||
export function nativeRemoveChild(
|
||||
renderer: Renderer3, parent: RElement, child: RNode, isHostElement?: boolean): void {
|
||||
isProceduralRenderer(renderer) ? renderer.removeChild(parent as RElement, child, isHostElement) :
|
||||
parent.removeChild(child);
|
||||
renderer: Renderer3, child: RNode, isHostElement?: boolean): void {
|
||||
if (isProceduralRenderer(renderer)) {
|
||||
const renderParent = renderer.parentNode(child);
|
||||
if (renderParent) {
|
||||
renderer.removeChild(renderParent, child, isHostElement);
|
||||
}
|
||||
} else {
|
||||
// We intentionally don't use the given parent node since it may no longer
|
||||
// match the state of the DOM (if the child node has been manually moved).
|
||||
child.parentNode && child.parentNode.removeChild(child);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -692,14 +698,9 @@ export function getBeforeNodeForView(index: number, views: LView[], containerNat
|
||||
* @param childTNode The TNode of the child to remove
|
||||
* @param childEl The child that should be removed
|
||||
* @param currentView The current LView
|
||||
* @returns Whether or not the child was removed
|
||||
*/
|
||||
export function removeChild(childTNode: TNode, childEl: RNode, currentView: LView): void {
|
||||
const parentNative = getRenderParent(childTNode, currentView);
|
||||
// We only remove the element if it already has a render parent.
|
||||
if (parentNative) {
|
||||
nativeRemoveChild(currentView[RENDERER], parentNative, childEl);
|
||||
}
|
||||
export function removeNode(childTNode: TNode, childEl: RNode, currentView: LView): void {
|
||||
nativeRemoveChild(currentView[RENDERER], childEl);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user