fix(dashboard): clean up iframe message listener on component destroy
- Dominant language
- TypeScript
- Stars
- 19
- Forks
- 68
- Avg merge
- 2d 16h
- Merged PRs (30d)
- 10
Description
### Checks
- [x] I have searched the [existing issues](https://github.com/kubeflow/dashboard/issues).
- [x] My issue is related to one of the components in the [`kubeflow/dashboard`](https://github.com/kubeflow/dashboard) repository.
### Kubeflow Version
kubeflow/dashboard @ 4b10796
### Kubeflow Platform
Local development of the kubeflow/dashboard repository; a full Kubeflow Platform installation was not used.
### Kubernetes Distribution
Not applicable — this frontend lifecycle issue was reproduced without a running Kubernetes distribution.
### Kubernetes Version
```shell
Not applicable — no Kubernetes cluster was required for the reproduction.
```
### Description
`IframeWrapperComponent` registers a global `message` event listener on `window` during `ngAfterViewInit()`, but the listener is not removed in `ngOnDestroy()`.
The listener is registered using an anonymous callback:
```ts
window.addEventListener(MESSAGE, ev => {
this.onMessageReceived(ev);
});
While ngOnDestroy() currently cleans up the URL subscription and polling interval, it does not remove this global event listener.
Reproduction
Open the iframe-backed route /_/jupyter.
Confirm that the active Angular RouterOutlet contains an IframeWrapperComponent.
Check the global message listeners:
getEventListeners(window).message?.length || 0
Observed: 1.
Deactivate the active Angular RouterOutlet without reloading the page:
const outlet = ng.getDirectives(document.querySelector('router-outlet'))[0];
outlet.deactivate();
Confirm that the outlet is no longer active:
outlet.isActivated
Observed: false.
Check the global message listeners again:
getEventListeners(window).message?.length || 0
Observed: 1.
The listener therefore remains registered on window after the IframeWrapperComponent has been deactivated.
Expected behavior
The global window message listener should be removed when IframeWrapperComponent is destroyed.
Actual behavior
The component is deactivated, but the global window message listener remains registered.
Because the listener callback closes over the component instance, this can retain the destroyed component and associated resources longer than necessary.
Suggested direction
Store the event-listener callback as a component property so the same function reference can be passed to both addEventListener() and removeEventListener() during component destruction.
Relevant file
components/centraldashboard-angular/frontend/src/app/pages/iframe-wrapper/iframe-wrapper.component.ts
### Relevant Logs
```shell
No relevant Kubernetes logs. This issue was reproduced in the local Dashboard frontend using Chrome DevTools and Angular's development-mode APIs.
```
Contributor guide
Research direction
Open components/centraldashboard-angular/frontend/src/app/pages/iframe-wrapper/iframe-wrapper.component.ts and inspect the listener registration in ngAfterViewInit() alongside cleanup in ngOnDestroy(). Reproduce the /_/jupyter route case and compare window message-listener counts before and after router-outlet deactivation. Done means the listener is removed after IframeWrapperComponent is destroyed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- angular, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100