kubeflow / kubeflow/dashboard

fix(dashboard): clean up iframe message listener on component destroy

Open Beginner friendly
#456 1 comment 0 reactions 0 assignees View on GitHub
kind/bug priority/needs-triage
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.