* Calls onDestroys hooks for all directives and pipes in a given view and then removes all * listeners. Listeners are removed as the last step so events delivered in the onDestroys hooks * can be propagated to @Output listeners. * * @param tView `TView` for the `LView` to clean up. * @param lVi
(tView: TView, lView: LView)
| 334 | * @param lView The LView to clean up |
| 335 | */ |
| 336 | function cleanUpView(tView: TView, lView: LView): void { |
| 337 | if (isDestroyed(lView)) { |
| 338 | return; |
| 339 | } |
| 340 | |
| 341 | const prevConsumer = setActiveConsumer(null); |
| 342 | try { |
| 343 | // Usually the Attached flag is removed when the view is detached from its parent, however |
| 344 | // if it's a root view, the flag won't be unset hence why we're also removing on destroy. |
| 345 | lView[FLAGS] &= ~LViewFlags.Attached; |
| 346 | |
| 347 | // Mark the LView as destroyed *before* executing the onDestroy hooks. An onDestroy hook |
| 348 | // runs arbitrary user code, which could include its own `viewRef.destroy()` (or similar). If |
| 349 | // We don't flag the view as destroyed before the hooks, this could lead to an infinite loop. |
| 350 | // This also aligns with the ViewEngine behavior. It also means that the onDestroy hook is |
| 351 | // really more of an "afterDestroy" hook if you think about it. |
| 352 | lView[FLAGS] |= LViewFlags.Destroyed; |
| 353 | |
| 354 | lView[REACTIVE_TEMPLATE_CONSUMER] && consumerDestroy(lView[REACTIVE_TEMPLATE_CONSUMER]); |
| 355 | |
| 356 | executeOnDestroys(tView, lView); |
| 357 | processCleanups(tView, lView); |
| 358 | // For component views only, the local renderer is destroyed at clean up time. |
| 359 | if (lView[TVIEW].type === TViewType.Component) { |
| 360 | lView[RENDERER].destroy(); |
| 361 | } |
| 362 | |
| 363 | const declarationContainer = lView[DECLARATION_LCONTAINER]; |
| 364 | // we are dealing with an embedded view that is still inserted into a container |
| 365 | if (declarationContainer !== null && isLContainer(lView[PARENT])) { |
| 366 | // and this is a projected view |
| 367 | if (declarationContainer !== lView[PARENT]) { |
| 368 | detachMovedView(declarationContainer, lView); |
| 369 | } |
| 370 | |
| 371 | // For embedded views still attached to a container: remove query result from this view. |
| 372 | const lQueries = lView[QUERIES]; |
| 373 | if (lQueries !== null) { |
| 374 | lQueries.detachView(tView); |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | // Unregister the view once everything else has been cleaned up. |
| 379 | unregisterLView(lView); |
| 380 | } finally { |
| 381 | setActiveConsumer(prevConsumer); |
| 382 | } |
| 383 | } |
| 384 | |
| 385 | /** Removes listeners and unsubscribes from output subscriptions */ |
| 386 | function processCleanups(tView: TView, lView: LView): void { |
no test coverage detected