refactor(core): only annotate disconnected nodes in content projection (#49549)

Previously, we've annotated all disconnected DOM nodes, even if they are not used in content projection. However, this situation is only possible in content projection and if it happens in other cases (for example, when a node was removed using direct DOM manipulations), this should be a mismatch error.

PR Close #49549
This commit is contained in:
Andrew Kushnir 2023-03-22 12:51:48 -07:00 committed by Andrew Scott
parent 03d1d00ad9
commit f1d3be3dee
2 changed files with 20 additions and 5 deletions

View file

@ -12,7 +12,7 @@ import {CONTAINER_HEADER_OFFSET, LContainer} from '../render3/interfaces/contain
import {TI18n} from '../render3/interfaces/i18n';
import {TNode, TNodeType} from '../render3/interfaces/node';
import {RElement} from '../render3/interfaces/renderer_dom';
import {isLContainer, isProjectionTNode, isRootView} from '../render3/interfaces/type_checks';
import {isComponentHost, isLContainer, isProjectionTNode, isRootView} from '../render3/interfaces/type_checks';
import {HEADER_OFFSET, HOST, LView, RENDERER, TView, TVIEW, TViewType} from '../render3/interfaces/view';
import {unwrapRNode} from '../render3/util/view_utils';
import {TransferState} from '../transfer_state';
@ -240,7 +240,7 @@ function serializeLView(lView: LView, context: HydrationContext): SerializedView
// layer (in Domino) for now. Longer-term solution should not rely on the DOM emulation and
// only use internal data structures and state to compute this information.
if (!(tNode.type & TNodeType.Projection) && !!lView[i] &&
!(unwrapRNode(lView[i]) as Node).isConnected) {
!(unwrapRNode(lView[i]) as Node).isConnected && isContentProjectedNode(tNode)) {
ngh[DISCONNECTED_NODES] ??= [];
ngh[DISCONNECTED_NODES].push(noOffsetIndex);
continue;
@ -413,3 +413,20 @@ function insertCorruptedTextNodeMarkers(
textNode.after(doc.createComment(marker));
}
}
/**
* Detects whether a given TNode represents a node that
* is being content projected.
*/
function isContentProjectedNode(tNode: TNode): boolean {
let currentTNode = tNode;
while (currentTNode != null) {
// If we come across a component host node in parent nodes -
// this TNode is in the content projection section.
if (isComponentHost(currentTNode)) {
return true;
}
currentTNode = currentTNode.parent as TNode;
}
return false;
}

View file

@ -3173,9 +3173,7 @@ describe('platform-server integration', () => {
});
});
// TODO(akushnir): we should only mark nodes within a content projection block as
// "disconnected" (avoid marking other disconnected nodes in a template)
xit('should handle element node mismatch', async () => {
it('should handle element node mismatch', async () => {
@Component({
standalone: true,
selector: 'app',