# Nested custom components issues (Devextreme and Aurelia 2.0 story)

**URL:** <https://discourse.aurelia.io/t/nested-custom-components-issues-devextreme-and-aurelia-2-0-story/6553>\
**Category:** Framework Knowledge\
**Created:** [December 9, 2025, 5:20am UTC](https://discourse.aurelia.io/t/nested-custom-components-issues-devextreme-and-aurelia-2-0-story/6553 "2025-12-09T05:20:50Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![estaylorco](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/estaylorco/32/1887_2.png) [@estaylorco](https://discourse.aurelia.io/u/estaylorco)\
**Post date:** [December 9, 2025, 5:20am UTC](https://discourse.aurelia.io/t/nested-custom-components-issues-devextreme-and-aurelia-2-0-story/6553/1 "2025-12-09T05:20:50Z")

</div>

Hello,

#### UPDATE #2

@bigopon I think I can move the needle on this now. It turns out that the Popup widget never did fire `dxremove` when the popup is hidden, even in our Aurelia 1.0 version. But with Aurelia 1.0, handling the `dxremove` event didn’t involve _disposing_ of the enhanced view. Here’s the code from the Aurelia 1.0 app:

```javascript
...

let newElement = element.cloneNode(true);

...

const view = this._createAureliaViewFromTemplate(
  newElement,
  itemBindingContext,
  itemOverrideContext,
  scope.owningView.resources,
);

...

dxEventOn(newElement, 'dxremove', () => {
  view.detached();
  view.unbind();
});

...

```

It would appear that Aurelia 1.0 handled the disposing of the resources itself. Compare this to Aurelia 2.0:

```javascript
...

const $template = rawTemplate.cloneNode(true);

...

const enhancedView = Aurelia.enhance({
  container: config.container,
  host: $template,
  component: {
    data: renderData.model,
  },
});

...

dxEventOn($template, 'dxremove', () => {  
  if (!enhancedView) {
    return;
  }

  onResolve(enhancedView.deactivate(enhancedView, config.controller), () => {
    enhancedView.dispose(); // <== And added step with Aurelia 2.0
    this.templates = undefined;
  });
});

...

```

The added step of having to dispose is where the hangup is. The inner custom elements (the content of the popup) are indeed attaching (I’ve confirmed that), but they’re not detaching because `dxremove` is never fired, and so I have no opportunity to dispose of the enhanced views for those inner templates. When the popup has `visible` set to `false`, the popup detaches, but none of the inner content does; hence the memory leaks.

**How do you recommend I handle the explicit disposal of the inner content? Where might I hook in?**

I think that solving this problem will address the memory leak issues.

#### UPDATE #1

I would like to put this on hold for just a moment. I’m not convinced that nested components are the problem.

I just determined that the Devextreme Popup widget doesn’t dispatch the `dxremove` event when the popup is hidden, either by clicking outside, or by clicking the close box. The `detaching` hook fires for the **tsi-dx-popup** itself, but not for the **popup-content** element it contains (the attaching hook does fire for **popup-content** ).

I think **popup-content** is left stranded, because navigating away from where it’s consumed causes a framework error indicating that `deactivate` is not available on `ehancedView`.

I’m going to reach out to Devextreme support.

#### ORIGINAL POST

In my Devextreme (Dx) showcase (which I just posted), in order to eliminate memory leaks, I actually have to remove components where templates that are ingested into Dx widgets contain other Devextreme widgets that in turn contain other Dx widgets, and so on. In addition, Dx widgets that “pop up” (whose visibility is controlled by the widget, not by template controllers) cause additional problems.

**The Specific Case of this Post: tsi-dx-popup**

The problems I outline below have to do with tsi-dx-popup (but apply to any widget where Dx widgets are nested). _I do believe that the leaking of memory has to do with the few popup-type widgets in the markup, which is why I’m focusing on solving my problems with them first._

**The Problems**

- In the case of the popup widget, lifecycle creation hooks are called (binding, attaching, etc.), but not teardown lifecycle hooks (I think this is due to the fact that the widget itself controls visibility through its `visible` option, which I bind back to the consuming component).
- Probably because of the former problem, when I navigate away from the showcase, I get an error (see below). This happens only when I show the popup (or popover, or tooltip, or toast), then hide it, then navigate away from the showcase.
- This is definitely causing memory to leak (apart from crippling the application). If I remove all of the popup-like custom elements from the markup, I have no leaks and no errors.

 ![Screenshot 2025-12-08 at 10.09.58 PM](https://canada1.discourse-cdn.com/flex027/uploads/aurelia/original/2X/e/e644993c6104050ae0a52dfb5b257e3b6d74ddc4.png)

 ![Screenshot 2025-12-08 at 10.10.31 PM](https://canada1.discourse-cdn.com/flex027/uploads/aurelia/original/2X/c/cc232ddec087c62474524ee14c6dd6eaf91bc65f.png)

Do you have any ideas what might be the problem? How do I handle the situation where a third-party component manages its own visibility?

#### PARTIAL SOLUTION

If I set the visible option to true on the popup widget, and then add an Aurelia `if`-binding to the widget, I can compel the teardown lifecycle hooks, but then I get this error when I close the popup:

 ![Screenshot 2025-12-08 at 10.17.00 PM](https://canada1.discourse-cdn.com/flex027/uploads/aurelia/original/2X/2/262d6708cfe263d9b38b6cb68ba0b9536857dce9.png)

#### THE CODE

Consider the following (from my dx-showcase markup):

```xml
<test-popup container=".dx-showcase" visible.two-way="showPopup"></test-popup>

```

This is typical approach (which I used in my Aurelia 1.0 app), where an application-level custom element wraps the Dx popup wrapper, which in turn wraps the Dx widget itself. **test-popup.html** contains the popup content in the form of a dx-template, and _that_ content contains other custom elements that wrap Dx widgets…and so on.

**test-popup.js** :

```javascript
import { bindable, BindingMode, customElement } from 'aurelia';

@customElement('test-popup')
export class TestPopup {
  @bindable({ mode: BindingMode.twoWay }) visible = true;
  @bindable({ mode: BindingMode.twoWay }) container = false;

  constructor() {
    this.showPopup = false;
  }

  onHidden() {
    this.visible = false;
  }

  ...

  visibleChanged(newValue) {
    this.showPopup = newValue;
  }
}

```

**test-popup.html** :

```xml
<import from="common/components/dx/tsi-dx-popup"></import>
<import from="modules/dx-showcase/popup-content"></import>

<tsi-dx-popup
  container.bind="container"
  visible.two-way="visible"  
  hidden.trigger="onHidden()"
>
  <tsi-dx-template name="content">
    <popup-content></popup-content>
  </tsi-dx-template>
</tsi-dx-popup>

```

**popup.content.js** :

```javascript
import { customElement } from 'aurelia';

@customElement('popup-content')
export class PopupContent {
  constructor() {
    this.text = 'Some value';
  }

  onClickButton(e) {
    alert('It worked!');
  }

  onValueChanged(e) {
    console.log('Value changed on popup!');
  }
}

```

**popup.content.html** :

```xml
<import from="common/components/dx/tsi-dx-button"></import>
<import from="common/components/dx/tsi-dx-text-box"></import>

<div value-changed.trigger="onValueChanged($event)">
  <tsi-dx-button text="Some Button"></tsi-dx-button>
  <tsi-dx-text-box value.bind="text"></tsi-dx-text-box>
</div>

```

---

<div class="post-metadata">

**Author:** ![bigopon](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/bigopon/32/18_2.png) [@bigopon](https://discourse.aurelia.io/u/bigopon)\
**Post date:** [December 10, 2025, 8:26pm UTC](https://discourse.aurelia.io/t/nested-custom-components-issues-devextreme-and-aurelia-2-0-story/6553/2 "2025-12-10T20:26:58Z")

</div>

Its a bit of a long post so I “saved” it for later the last weekend and completely forgot. I guess the new post you have is where we discuss this?

---

<div class="post-metadata">

**Author:** ![estaylorco](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/estaylorco/32/1887_2.png) [@estaylorco](https://discourse.aurelia.io/u/estaylorco)\
**Post date:** [December 10, 2025, 9:04pm UTC](https://discourse.aurelia.io/t/nested-custom-components-issues-devextreme-and-aurelia-2-0-story/6553/3 "2025-12-10T21:04:34Z")

</div>

I think we can discuss it in the new post, yes. But there’s information in this one that might help.

Sorry for the long posts, but it’s an interesting—and complex—migration story I think for those who are using `enhance` outside the mainstream use cases, and who are migrating from 1.0 to 2.0!
