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