( tView: TView, tNode: TNode | null, lView: LView, )
| 488 | * @param lView: Current `LView`. |
| 489 | */ |
| 490 | export function getParentRElement( |
| 491 | tView: TView, |
| 492 | tNode: TNode | null, |
| 493 | lView: LView, |
| 494 | ): RElement | null { |
| 495 | // `tNode` can genuinely be null here, not just as a defensive type-widening measure. An |
| 496 | // `@if`/`@switch` branch's content is its own embedded view with its own `TView`, built the |
| 497 | // first time that branch is rendered. If an error interrupts that first creation pass (for |
| 498 | // example, a hydration mismatch on one of the branch's later nodes, after an earlier node's |
| 499 | // `TNode` was already created), `TView.firstCreatePass` still gets flipped to `false` before |
| 500 | // the error propagates (see `renderView()`'s `catch` block in `instructions/render.ts`), |
| 501 | // permanently marking the view's `TNode` data as "already created" even though it isn't. |
| 502 | // Unlike a component's `TView`, nothing rebuilds an embedded view's `TView` afterward, so the |
| 503 | // next time that same branch is selected, its instructions read directly from the still-null |
| 504 | // slot in `tView.data` instead of creating a fresh `TNode` — which is what surfaces here. |
| 505 | // Guard against it so production throws a coded RuntimeError instead of a raw TypeError when |
| 506 | // dereferencing `tNode.parent` below. |
| 507 | if (tNode === null) { |
| 508 | throw new RuntimeError( |
| 509 | RuntimeErrorCode.PARENT_NODE_NOT_FOUND, |
| 510 | ngDevMode && |
| 511 | 'getParentRElement() was called with a null TNode, so no parent element could be resolved. This usually means a TNode was never created for this node, or was already destroyed.', |
| 512 | ); |
| 513 | } |
| 514 | return getClosestRElement(tView, tNode.parent, lView); |
| 515 | } |
| 516 | |
| 517 | /** |
| 518 | * Get closest `RElement` or `null` if it can't be found. |
no test coverage detected