# Guidance Needed: I've gotten Aurelia 2.0 templates (through enhance) to work with Devextreme's widget integration point

**URL:** <https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544>\
**Category:** Uncategorized\
**Created:** [November 25, 2025, 4:07am UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544 "2025-11-25T04:07:14Z")\
**Posts on this page:** 13\
**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:** [November 25, 2025, 4:07am UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/1 "2025-11-25T04:07:14Z")

</div>

Hello Everyone,

@bigopon @Sayan751

_(For this post **tsi-dx-button** and **my-dx-button** are synonymous)._

Following on from a topic I posted at the beginning of the year ([Migrating Au1's #enhance to Au2's #enhance](https://discourse.aurelia.io/t/migrating-au1s-enhance-to-au2s-enhance/6321)), I was finally able to get Aurelia 2.0 and Devextreme to play well in terms of templating (so that a Devextreme widget hosts one or more `dx-template`s as Aurelia-enhanced HTML fragments). I had this working flawlessly in Aurelia 1.0 with no memory leaks, but have had trouble getting to that same level in Aurelia 2.0.

> NOTE: I’m not referring to simply providing a render function to a template prop (such as `itemTemplate`, `fieldTemplate`, `rowTemplate`, etc.). I’m referring to providing templates in the markup (HTML) with `dx-template` tags, harvesting those fragments, enhancing them, removing them from the DOM, and then supplying them to a Devextreme widget through the `integrationOptions` prop (according to the Devextreme documentation).

#### BACKGROUND

Consider the following example ( **my-dx-button.html** ):

```xml
<my-dx-button text.bind="userValue" click.trigger="handleButtonClick()">
    <dx-template name="content">
        <div class="tsi-test-class">Button: ${data.text}</div>
    </dx-template>
</my-dx-button>

```

The **my-dx-button** component wraps Devextreme’s **Button** widget. **Button** is one of those widgets that has a `*template` prop, the default name of which is _content_. There can be an unlimited number of `dx-template`s, all with different names, and either targeting different props of the widget, or offering polymorphic options where one will be selected at runtime (think of a template selector pattern).

#### FIRST PASS

This first pass doesn’t address events yet, and it involves only the **Button** ’s `text` prop. Also, I’m not at a point where I can begin to factor all of this out into a service, as so forth. I would appreciate commentary on my approach here, which is essentially the same as the approach I took with Aurelia 1.0.

> NOTE: Devextreme calls the `render` function associated with the template in the `templateDict` each time data changes, which precipitates an Aurelia-based re-enhancement of the HTML.

Here’s the entirety of **my-dx-button.js** , with code comments:

```javascript
import Aurelia, { bindable, customElement, inject, processContent, resolve } from 'aurelia';
import { IInstruction } from '@aurelia/template-compiler';

import Button from 'devextreme/ui/button';
// This is my own model of the dx-template
import { DxTemplateModel } from './DxTemplateModel';

// processContent callback
function harvestDxTemplates(node, _, instructionData) {
    instructionData.templates = node.querySelectorAll('dx-template');

    // Now that we've harvested the dx-templates from the DOM, we don't need them anymore; we must do this here in the processContent callback
    instructionData.templates.forEach((_template) => {
        node.removeChild(_template);
    });

    // Signals the absence of content when we're done
    // Equivalent to processContent(false) in Aurelia 1.0
    return false;
}

@customElement({
    name: 'tsi-dx-button',
    template: null, // Equivalent to @noView in Aurelia 1.0
})
@processContent(harvestDxTemplates)
@inject(Element, CustomEventDispatcher)
export class MyDxButton {
    // Curated bindable for this example
    @bindable text = 'Default Text';

    constructor($el, eventDispatcher) {
        this.$el = $el;
        this.eventDispatcher = eventDispatcher;

        this.widgetHost;
        this.widgetInstance;

        this.templateDict = {};
        this.enhancedTemplates = [];

        const { data } = resolve(IInstruction);
        this.templates = data.templates; // Pushed into the instructionData from harvestDxTemplates above

        this.templates.forEach((_template) => {
            this.ingestTemplate(_template);
        });

    binding() {
        // With Devextreme, NEVER host right off of Element, which is this.$el above; we host one additional level down instead
        this.widgetHost = document.createElement('div');
        this.$el.appendChild(this.widgetHost);

        // According to the Devextreme documentation
        const integrationOptions = {
            templates: this.templateDict, // Built up in the constructor where we iterate over the harvested templates and ingest them
        };

        // Mount Devextreme component on the widget DOM host
        this.widgetInstance = new Button(this.widgetHost, {
            template: 'content',           
            text: this.text,
            type: this.type ?? 'default',
            integrationOptions: integrationOptions, // Devextreme's integration point
        });
    }

    unbinding() {
        this.enhancedTemplates.forEach((_template) => {
            _template.dispose();
        });

        this.enhancedTemplates.splice(0);
        this.enhancedTemplates = null;

        this.widgetInstance.dispose(); // If we don't do this, we'll leak memory
        this.widgetHost.remove(); // If we don't do this, we'll leak memory
        this.widgetInstance = null;
        this.widgetHost = null;

        this.templateDict = null;
    }
  
    // TODO: Train Aurelia how to bind to a Devextreme component so we can eliminate the need for this...possibly
    textChanged(newValue) {
        this.widgetInstance.option('text', newValue);
    }

    ingestTemplate(template) {
        // Devextreme expects dx-templates to have a name
        const templateName = template.getAttribute('name');
        const templateRenderer = (renderData) => {
            return this.createRenderer(template, renderData.container, renderData.model);
        };

        // Devextreme expects, simply, that for each named template, we have a render function assigned to the *render* property;
        // This is the dictionary we pass to the Devextreme component at the integration point
        this.templateDict[templateName] = {
            render: templateRenderer,
        };
    }

    createRenderer(rawTemplate, container, model) {
        // Append the harvested raw markup (template) to the container provided by Devextreme, and then enhance it
        const $enhancementHost = rawTemplate.cloneNode(true);
        container.appendChild($enhancementHost);

        const templateModel = new DxTemplateModel();
        templateModel.data = model;

        templateModel.templateName = rawTemplate.getAttribute('name');
        const enhancedTemplate = Aurelia.enhance({
            host: $enhancementHost,
            component: templateModel,
        });

        const ti = this.enhancedTemplates.findIndex(
            (_template) =>
                _template.controller.viewModel.templateName ===
                enhancedTemplate.controller.viewModel.templateName
        );

        if (ti >= 0) {
            this.enhancedTemplates[ti].dispose();
            this.enhancedTemplates.splice(ti, 1);
        }

        this.enhancedTemplates.push(enhancedTemplate);

        // // Register a Dx remove event, dxremove, so that we have an opportunity
        // // to call the Aurelia "destroy" lifecycle callbacks (careful to call
        // // #detaching first, then #unbinding, then #dipose)
        // dxEventOn($enhancementHost, 'dxremove', () => {
        // enhancedTemplate?.detaching?.();
        // enhancedTemplate?.unbinding?.();
        // enhancedTemplate?.dispose?.(); // If we don't call this, we'll leak memory
        // });

        // Return the host of the enhancement
        return $enhancementHost;
    }
}

```

#### QUESTIONS AND PROBLEMS

My solution above suffers from the following problems:

- I’m leaking memory, which I suspect comes from how I’m storing the enhanced templates for disposal later
- I’m unable to use custom components within the `dx-template`, whether I register the custom components globally or locally
- The `DxTemplateModel`’s lifecycle hooks never get called, nor does its constructor

**DxTemplateModel.js** looks like this:

```javascript
export class DxTemplateModel {
    constructor() {
        this.templateName = '';
        this.data = null;
    }

    unbinding() {
        console.log('DX template model unbinding...');
    }
}

```

As for my questions:

- In Aurelia 1.0, one has to register a `dxremove` event on the enhancement host in order to tear down the component associated with the `dx-template` (Devextreme fires the event and we can then hook into it). Is this necessary in Aurelia 2.0? `dxremove` fires, but I don’t know how to cause the lifecycle hooks to fire.
- Am I handling the enhanced templates correctly by storing them on the instance, and then iterating over them to dispose of each one?
- How do I get custom components to work within the enhanced templates?

Concerning the latter question, consider the following in the About page, which is where I’m testing my-dx-button:

```xml
<import from="./tsi-dx-button"></import>
<!-- <import from="./button-text-provider"></import> -->

<h1>The about landing page.</h1>

<input value.bind="userValue" />

<tsi-dx-button
    text.bind="userValue"
    click.trigger="handleButtonClick()"
>
    <dx-template name="content">
        <div class="tsi-test-class">Button: ${data.text}</div>
        <!-- <button-text-provider text.bind="data.text"></button-text-provider> -->
    </dx-template>
</tsi-dx-button>

```

I can’t get the commented code above to work. When I supply the button’s text with a custom component, it doesn’t work.

Here is a GIF to show that the uncommented code is working (the HTML with `${data.text}` as the text):

![Aurelia 2.0 and Devextreme](https://canada1.discourse-cdn.com/flex027/uploads/aurelia/original/2X/8/85d29d5f6642f962eacc4b392e99def7c989853a.gif)

I set up a two-way binding with an input just to prove to myself that I can effect a change of the button’s text. Changing the text causes the `textChanged` handler to fire on the wrapper, a handler that uses the Devextreme widget’s API to push the change into the widget, which then causes the template render function to be called by the widget.

> NOTE: The `dx-template` HTML fragment that you’re seeing in the inspector at the right is the version that is reinserted by Devextreme (which is Aurelia-enhanced by then), not the original `dx-template` in the markup that is harvested, removed, and then processed by the `processContent` callback.

I would appreciate commentary and insight. I’m so close!

Thank you in advance!

---

<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:** [November 27, 2025, 8:15am UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/2 "2025-11-27T08:15:17Z")

</div>

@estaylorco it’s a bit of a long post, so maybe we should have a few back&forth to go over the points you wanted to talk about.

First & foremost issue I see is this

```auto
Aurelia.enhance({ ... })

```

You are creating a new aurelia instance, along with its own new container. This means whatever registered globally or locally won’t ever reach the enhanced template. What could have been done is:

```auto
class MyComponent {
    aurelia = resolve(Aurelia);

    render() {
        this.aurelia.enhance({ ... })
        // or
        // Aurelia.enhance({ container: this.aurelia.container })
    }
}

```

The key is to pass the right container to the `.enhance` call for looking up resources.

---

<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:** [November 27, 2025, 6:35pm UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/3 "2025-11-27T18:35:14Z")

</div>

Hello,

**(Please see my UPDATE #1 in the form of a reply to this post).**

So my understanding grew considerably in the few days following that long post. I apologize for the length, but I wanted to do a brain dump on this issue. Also, thank you for all of your posts at the beginning of the year, including the `dx-report` post. All of them gave me food for thought and guided me in coming to a better understanding.

I’ve included below my currently working Au2 solution using synthetic views. **I’ve also included my working Au1 approach at the bottom of this post, and there’s where I pose my question to you**.

Just some points since that post:

- I came to realize that I was missing the container, and adding it actually got me to a working solution (albeit with some issues);
- I have since watched Dwayne’s wonderful YouTube video on synthetic views, to which I have now switched (and away from `enhance`);
- I’ve commented out the `processContent` code (and, therefore, harvesting `dx-template`s from the markup), for now, in favor of synthesizing the view directly in a template render function (without using **Devextreme** ’s `integrationOptions` integration point);
- I will return to harvesting `dx-template`s (using `processContent`) from markup once I understand how to pass local resources to either synthetic views or `enhance` (_I’ve reached the point now where I can harvest templates from markup using the example you gave earlier in the year as a guide, remove them from the DOM, etc., successfully_);
- I fully understand that, with a synthetic view created from an inline template, I would have to globally register my resources (or create an _ad hoc_ DI container and register them in it); but I’ll be moving back to harvesting templates from markup as that is what I prefer.

#### WORKING VERSION (SYNTHETIC VIEW, INLINE TEMPLATE, AND RENDER FUNCTION)

Consider what I have now in Au2, **which is actually working without memory leaks** , as far as I can tell:

```javascript
import { bindable, customElement, IContainer, inject, resolve, Scope } from 'aurelia';
import {
    convertToRenderLocation,
    CustomElement,
    CustomElementDefinition,
    IController,
    ViewFactory,
} from '@aurelia/runtime-html';

import Button from 'devextreme/ui/button';
import { on as dxEventOn } from 'devextreme/events';

@customElement({
    name: 'tsi-dx-button',
    template: null,
})
@inject(Element)
export class TsiDxButton {
    // Curated bindable for this exploration
    @bindable text = 'Default Text';

    constructor($el) {
        this.$el = $el;       

        this.widgetHost;
        this.widgetInstance;
        this.view;

        this.container = resolve(IContainer);
        this.$controller = resolve(IController);
    }

    attached() {
        this.widgetHost = document.createElement('div');
        this.$el.appendChild(this.widgetHost);        

        // Mount Devextreme component on the widget DOM host
        this.widgetInstance = new Button(this.widgetHost, {
            template: (data, element) => {
                const $template = document.createElement('template');
                $template.innerHTML = `<button-text-provider text.bind="data.text"></button-text-provider>`;

                element.append($template);

                const renderLocation = convertToRenderLocation($template);

                const viewFactory = new ViewFactory(
                    this.container,
                    CustomElementDefinition.create({
                        name: CustomElement.generateName(),
                        template: $template,
                    })
                );

                // Register a Dx remove event, dxremove
                dxEventOn(element, 'dxremove', () => {
                    if (!this.view) {
                        return;
                    }

                    this.view.deactivate(this.view, this.$controller);
                    this.view.dispose();
                    this.view = undefined;
                });

                this.view = viewFactory.create(this.$controller).setLocation(renderLocation);
                const viewModel = {
                    data: data,
                };

                this.view.activate(this.view, this.$controller, Scope.create(viewModel));
            },
            text: this.text,
            type: this.type ?? 'default',            
        });
    }

    unbinding() {
        this.widgetInstance.option('template', undefined);
        this.widgetInstance.dispose();
        this.widgetHost.remove();
    }

    // TODO: Train Aurelia how to bind to a Devextreme component so we can eliminate the need for this...possibly
    textChanged(newValue) {
        this.widgetInstance.option('text', newValue);
    }

```

#### QUESTION AND PREFERRED APPROACH (TAKEN FROM AU1 SOLUTION)

Consider this from my Au1 app, a component that represents popup content (with some elisions for brevity and clarity):

```xml
<template>
    <require from="./bid-pricing-line-item.css"></require>
    <require from="./tsi-model-editor-command-bar.css"></require>
    <require from="common/elements/dx/tsi-dx-popup"></require>
    <require from="common/elements/dx/tsi-dx-button"></require>

    <tsi-dx-popup title="Too Many Levels">
        <dx-template name="title">TOO MANY LEVELS</dx-template>
        <dx-template name="content">            
            <div class="tsi-dialog__buttons-wrapper">
                <div class="tsi-dialog__buttons">
                    <tsi-dx-button type="default" text="OK">
                    </tsi-dx-button>
                </div>
            </div>
        </dx-template>
    </tsi-dx-popup>
</template>

```

In the second `dx-template`, I reference **tsi-dx-button** and require it locally. In my Au1 templating bridge (between Au and Devextreme), I harvest the `dx-template`s and then remove them, as we’ve discussed, but I pass the `owningView`’s local resources to `enhance`, which are the view resources collocated with the `dx-templates`.

This is a natural and preferred way for me.

**My question** , which was the one at the beginning of the year: How do we do that now? Globally registering, say, **tsi-dx-button** , simply because it’s referenced in a dx-template that’s highly local seems awkward.

Also, creating a DI container and registering resources would work, yes, but I think would defy encapsulation. I would have to pass those resources in from the outside of what will be my new templating bridge (Au2 version). I shouldn’t have to do that if the resources are sitting there in the markup and collocated with the `dx-template`s.

---

<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:** [November 27, 2025, 9:10pm UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/4 "2025-11-27T21:10:21Z")

</div>

#### **UPDATE #2 (WORKING VERSION - REGISTERING RESOURCES ON THE CHILD CONTAINER)**

You can see from my `DxTemplateService` in Update #1 below that I can now supply resources to my `compileTemplates` method and register them in the child container created in the constructor.

That works, but it’s less than desirable because I have to pass those resources in twice (once through a bindable on the Devextreme widget wrapper, and once from the wrapper to the template service I’ve factored out). I would have to do something like this on the wrapper:

```xml
<tsi-dx-button button-text.bind="userValue" view-resources.bind=[...]></tsi-dx-button>

```

which then would get passed to my template service:

```javascript
compileTemplates(rawTemplates, resources = [], templateName) {
        if (resources && resources.length) {
            this.container.register(resources);
        }
        ...
}

```

Ideally, I’d like to simply import resources in the markup where the `dx-template`s are defined, and then let my service pick those up (in Au1, I express this as `owningView.viewResources`, which gets passed into `enhance`).

I would think that `inheritParentResources` would pull that off, but it doesn’t. I still have to explicitly register (in this case, `ButtonTextProvider`).

**Question** : Referencing **about-page.html** below, which is the owning view of `dx-template`, **tsi-dx-button** , or **about-page**?

#### UPDATE #1 (WORKING VERSION - processContent and Aurelia.enhance)

I have moved back to `processContent` and harvesting `dx-template`s from markup, and am no longer taking the synthetic-view approach.

This is what I have for the use case:

**about-page.html**

```xml
<import from="./tsi-dx-button"></import>
// This import is ignored (supplied instead via registration against the child container)
<import from="./button-text-provider"></import>

<h1>The about landing page.</h1>
<input value.bind="userValue" />

<tsi-dx-button button-text.bind="userValue">
    <dx-template name="content">
        <div style="background-color: yellow !important">
            <button-text-provider button-text.bind="data.text"></button-text-provider>
        </div>
    </dx-template>
</tsi-dx-button>

```

For the Devextreme **Button** wrapper (viewless):

**tsi-dx-button.js**

```javascript
import { bindable, customElement, IContainer, inject, resolve } from 'aurelia';
import { IInstruction } from '@aurelia/template-compiler';

import harvestDxTemplates from './harvestDxTemplates.js';
import { DxTemplateService } from './DxTemplateService.js';

import Button from 'devextreme/ui/button';
// This is simply to test a custom element in a dx-template
import { ButtonTextProvider } from './button-text-provider';

@customElement({
    name: 'tsi-dx-button',
    template: null, // Equivalent to @noView in Aurelia 1.0
    processContent(node, _, instructionData) {
        return harvestDxTemplates(node, _, instructionData);
    },
})
@inject(Element, DxTemplateService)
export class TsiDxButton {
    // Curated bindable for this proof of concept
    @bindable buttonText = 'Default Text';

    constructor($el, templateService) {
        this.$el = $el;
        this.templateService = templateService;

        this.widgetHost;
        this.widgetInstance;

        const { data } = resolve(IInstruction);
        this.templates = data.templates;
    }

    async attached() {
        this.widgetHost = document.createElement('div');
        this.$el.appendChild(this.widgetHost);

        const integrationOptions = {
            templates: this.templateService.compileTemplates(this.templates, [ButtonTextProvider]),
        };

        this.widgetInstance = new Button(this.widgetHost, {
            text: this.buttonText,
            type: this.type ?? 'default',
            integrationOptions, // Devextreme's integration point
        });
    }

    unbinding() {
        this.widgetInstance.dispose();
        this.widgetHost.remove();
    }

    // TODO: Train Aurelia how to bind to a Devextreme component so we can eliminate the need for this...possibly
    buttonTextChanged(newValue) {
        this.widgetInstance.option('text', newValue);
    }
}

```

Finally, this is my template bridge service:

**DxTemplateService.js**

```javascript
import { IController } from '@aurelia/runtime-html';
import Aurelia, { IContainer, resolve, transient } from 'aurelia';

import { on as dxEventOn } from 'devextreme/events';

@transient()
export class DxTemplateService {
    constructor() {
        this.container = resolve(IContainer).createChild({ inheritParentResources: true });
        this.$controller = resolve(IController);
    }

    compileTemplates(rawTemplates, resources = [], templateName) {
        if (resources && resources.length) {
            this.container.register(resources);
        }

        const templateDict = {};

        rawTemplates.forEach((_template) => {
            this.ingestTemplate(_template, templateDict, templateName);
        });

        return templateDict;
    }

    ingestTemplate(rawTemplate, templateDict, templateName) {
        // Devextreme expects DX-TEMPLATEs to have a name
        const _templateName = templateName ?? rawTemplate.getAttribute('name');
        const templateRenderer = (renderData) => {
            // renderData is kicked out by the Devextreme widget
            return this.createRenderer(rawTemplate, renderData.container, renderData.model);
        };

        // Devextreme expects, simply, that for each named template, we have a render function assigned to the *render* property;
        // This is the dictionary we pass to the Devextreme component at the integration point
        templateDict[_templateName] = {
            render: templateRenderer,
        };
    }

    createRenderer(rawTemplate, container, model) {
        const $template = rawTemplate.cloneNode(true);
        container.appendChild($template);

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

        // Register a Dx remove event, dxremove
        dxEventOn($template, 'dxremove', async () => {
            if (!enhancedView) return;

            await enhancedView.deactivate(enhancedView, this.$controller);
            if (!enhancedView) return;

            enhancedView.dispose();
        });

        return $template;
    }
}

```

---

<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:** [November 28, 2025, 10:44pm UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/5 "2025-11-28T22:44:21Z")

</div>

> [@estaylorco](#):
>
> ```auto
> @inject(Element, DxTemplateService)
> 
> ```

Nice work figuring it out 👍

For

```typescript
@inject(Element, DxTemplateService)

...
@transient()
class DxTemplateService { ... }

```

you can also do

```typescript
@inject(Element, @newInstanceOf(DxTemplateService))

```

* * *

For

```typescript
            await enhancedView.deactivate(enhancedView, this.$controller);
            if (!enhancedView) return;

            enhancedView.dispose();

```

if you ever have `detached` or `unbound` returning a promise, you’ll need to wait for it before disposing.

```typescript
import { onResolve } from 'aurelia';
...

            onResolve(enhancedView.deactivate(enhancedView, this.$controller),
              () => enhancedView.dispose()
            );

```

* * *

For

> Also, creating a DI container and registering resources would work, yes, but I think would defy encapsulation

Did you mean that the resources used inside a template will have to be registered by the places that consume it? In your last working version, it’s kind of ok because that’s where the resources should be registered. Like the `button-text-provider` as an example.

---

<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:** [November 29, 2025, 1:03am UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/6 "2025-11-29T01:03:41Z")

</div>

Excellent pointers on other ways to do part of what I’m doing, and I really need to look at what you’re doing with `dispose` on the enhanced view.

So, `ButtonTextProvider` was just a contrived proof of concept where passing in resources works because of the simplicity and contrivance.

**The last piece of the puzzle** is this, then. Consider the following usage site:

**about-page.html**

```xml
<import from="./tsi-dx-button"></import>
<import from="./button-text-provider"></import> 

<tsi-dx-button button-text.bind="buttonText">
    <dx-template name="content">
        <button-text-provider button-text.bind="data.text">
        </button-text-provider>
    </dx-template>
</tsi-dx-button>

```

I simply wish to import the resources where the `dx-template`s are defined, as you can see with **button-text-provider** , and then pick them as `owningView` resources at the point in my code where I enhance. In Au1, **about-page.html** was considered the `owningView`, which I would get a hold of in the `created` lifecycle hook, store it on the instance (in this case, my Devextreme widget wrapper) at `this.owningView`, and then just pass in `this.owningView.resources` to enhance as `viewResources`.

I don’t want to have to do this:

```javascript
integrationOptions = {
    templates: this.templateService.compileTemplates(
        this.templates, 
        [ButtonTextProvider]
    )};

```

**I guess we can’t do this in Au2:**

```javascript
const enhancedView = Aurelia.enhance({
    container: this.container,
    host: $template,
    component: {
        data: model,
    },
    viewResources: this.owningView.viewResources // <== this, ideally
});

```

That’s why I was asking back in January about what happened to `viewResources`. It seemed like that piece of the puzzle was missing.

#### QUESTION

How can achieve this in Au2?

---

<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 1, 2025, 12:35pm UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/7 "2025-12-01T12:35:56Z")

</div>

what you are doing is similar like what the `<au-slot>` is doing. I think you can just remove process content and use `projections` from the `IInstruction`. Doing it this way you have the right encapsulation: `button-text-provider` should be declared in the consuming component, not the “slotted” component. The code may look something like this:

```typescript
class MyButton {
  instruction = resolve(IInstruction);
  
  render() {
    const compiledContentTemplate = instruction.projections.default;
  }
}

```

In the block above, `compiledContentTemplate` is compiled with the right resources.

Once you have the compiled template, you can create view your way, or pass it as the component for the enhance call, or get the factory and chunk out views as needed etc…

I would probably just go for the enhance since it’s the least involved.

---

<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 1, 2025, 10:20pm UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/8 "2025-12-01T22:20:45Z")

</div>

Thank you for getting back to me, but I’m getting a little frustrated…

The solution you’ve provided won’t work, for a number of reasons:

- My wrappers are viewless (Devextreme widgets take over the DOM and handle the view);
- While what I’m doing might _look like_ slotted content, it’s not (the templates are removed from the DOM in `processContent` after harvesting, which isn’t something we do with slotted content);
- If by “consuming component” you mean my wrapper component around a Devextreme widget, well they’re general-purpose and have no view, as I said, so the content cannot go there;
- If by “slotted component” you mean the usage site of my “consuming component”—where the consuming component is actually used—that’s what Au1 called _owningView_;

I would point out that _owning_ and _parent_ are not necessarily synonymous, as you’ve hit upon by recognizing a quasi-slotted scenario.

No matter what path I take to `enhance`, if I show up a the doorstep without `viewResources`, without a means to pull them in, and no API by which to provide them to `enhance`, I wind up with an obtrusive solution. It’s basically PLATFORM, but spelled a little differently. I’m now having to tell Aurelia about something it should already know (and did in Au1).

**You have removed the viewResources API from enhance and eliminated access to the owning view, both of which are present in Au1. I don’t think you should have done that.** That makes `enhance` Au2 fundamentally different, and impaired, compared to `enhance` Au1, _no matter how much better Au2’s_ `enhance` _is technically._

I have roughly half of the Devextreme widgets now wrapped and working properly in a sort of showcase app (a testbed, if you will). But this is what I have wherever I need to push Aurelia templates into Devextreme widget at the `integrationOptions` key (where **b-t-p** is short for **button-text-provider** ):

**welcome-page.html**

```xml
...
<tsi-dx-button type="default"
    text.bind="text"
    click.trigger="onClickButton($event)"
    option-changed.trigger="onOptionChanged($event)"    
        template-resources.bind="templateResources" <= anti-pattern
    >
        <tsi-dx-template name="content">
            <b-t-p text.bind="data.text"></b-t-p>
        </tsi-dx-template>
</tsi-dx-button>
...

```

On the component (formerly, viewModel), I have this:

**welcome-page.js**

```javascript
this.templateResources = [ButtonTextProvider]; // <= anti-pattern

```

Within my wrapper, **tsi-dx-button** (actually, its base class), I have to do this:

**via TsiDxWidgetBase.js**

```javascript
...
async attached() {
    ...
    check: Array.isArray(this.templateResources), 'Template resources is of type Array';
    const compilerOptions = {
      resources: this.templateResources, // <= anti-pattern
      container: this.diContainer,
      controller: this.$controller,
    };

    // Compile templates, if there are any
    this.integrationOptions = this.templateService.compileTemplates(compilerOptions);
    ...
}

```

---

<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 1, 2025, 10:30pm UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/9 "2025-12-01T22:30:52Z")

</div>

If we had a template like this

```xml
// about-page.html
<my-component>
  <my-slotted-content>

```

then `about-page` is the consuming component, or owning component, and `my-component` is the slotted component, or consumed component, and everything inside `<my-component>`is slotted content. Maybe the names I used could be confusing, we can just use the pair consuming/consumed or owning/consumed.

> My wrappers are viewless (Devextreme widgets take over the DOM and handle the view);

What I mentioned was about getting a compiled template, with proper resources. How to create views from that template is your choice, if you have a Stackblitz repro (use this base [https://stackblitz.com/edit/au2-conventions-tvyh8c?file=src%2Fmy-app.html](https://stackblitz.com/edit/au2-conventions-tvyh8c?file=src%252Fmy-app.html)), I should be able to make it clearer.

> **You have removed the viewResources API from enhance and eliminated access to the owning view**

Owning view is called `hydration context` in v2, and can be injected by `IHydrationContext`. The property `controller` of a hydration context points to the owning view. It’s actually better in v2, where you don’t have to wait until `created` lifecycle to do something.

For the part “removed the viewResources API” from enhance, I don’t think that’s true. Whatever you did with viewResources, now should be done with `container.register()`, it’s practically the same thing. The only difference is one should not register new resources with the container of some other components.

* * *

For

```typescript
class AboutPage {
    ...
    this.templateResources = [ButtonTextProvider]; // <= anti-pattern
}

```

I don’t see it as an anti pattern. About page is what uses `ButtonTextProvider`, so the resources should be declared there.

---

<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 2, 2025, 2:08am UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/10 "2025-12-02T02:08:36Z")

</div>

#### UPDATE #1

_(How I got to this update is given down below)._

I just realized, and determined, that I don’t even have to register `ButtonTextProvider` in **about-page.js**!

This alone

```javascript
...
this.diContainer = resolve(IContainer).parent.createChild({inheritParentResources: true });
...

```

in **tsi-dx-widget-base.js** made the resources available to `enhance` (since I pass the `diContainer` above to my template compiler encapsulated in `DxAuTemplateService`).

This is what I see now:

 ![Screenshot 2025-12-01 at 7.14.04 PM](https://canada1.discourse-cdn.com/flex027/uploads/aurelia/original/2X/9/9b8c72fc6490fffb10740c2a09410b11b4d14b6c.jpeg)

Take a look at the first one!

But I’m still hoping for a better way than

```javascript
...
this.diContainer = resolve(IContainer).parent.createChild({inheritParentResources: true });
...

```

\*\*\*\*\*\*\*\*

I was out on my two-hour walk just now when your reply came in. I pulled over to read it, and as I was walking, it all clicked…I think! **I will need your advice here** (because I think my solution is a bit awkward).

As soon as I got back here to the office, I implemented what I had in mind based on a number of things you said. **It worked** …and I was able to eliminate the `templateResources` `@bindable`, which was the obtrusion that was bothering me.

So, then, this is where I am. Let’s go all the way up to **about-page.js** (the consuming or owning component, as you say). I did this there:

```javascript
this.container = resolve(IContainer).register(ButtonTextProvider);

```

With that, I’m registering in the consuming component. This is the context where I would have perfect knowledge of the dependencies. Whether I express them in code (above in **about-page.js** ), or in **about-page.html** as imports, I still have to express them.

Now in **tsi-dx-button.js** (actually, **tsi-dx-widget-base.js** , which **tsi-dx-button** extends), I have this in the constructor:

```javascript
...
this.diContainer = resolve(IContainer).parent.createChild({inheritParentResources: true });
...

```

When I did this and poked around the container instance, I saw a property called `res`…and in it was my **button-text-provider** component! NOW I understand the purpose and usefulness of `inheritParentResources`.

#### QUESTION

There has to be a more elegant approach to

```javascript
... = resolve(IContainer).parent.createChild({...})

```

What should I be doing?

---

<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 2, 2025, 3:05am UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/11 "2025-12-02T03:05:48Z")

</div>

`container.parent`does not guarantee that you will always get the container of the owning view. It’s better that you start using `IHydrationContext`, for the it’s the context about the owning view, where you have the right resources etc…

So the code would look like this

```typescript
resolve(IHydrationContext).controller.container.createChild({ inheritParentResources: true });

```

and with that, you kind of get an idea what au-slot is doing internally.

---

<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 2, 2025, 3:22am UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/12 "2025-12-02T03:22:51Z")

</div>

That did it!

Now I understand what you said earlier when you said that `viewResources` is _consolidated_. I couldn’t get my mind around why you were using that word. I no longer have to _supply_ the `viewResources` to `enhance`…they’re just there in the container so long as I bring them in through `inheritParentResources`. The trick is to get the right container for the context.

I’m still not too fond of having to work with the container directly in this way. But at least I can encapsulate access into a base class, as I have done in this case.

Thank you for all of your work in helping me out, and the community since this journey is recorded here, and for sticking with me on this! This was really helpful as it allowed me to see some of the more technical aspects of the framework.

I will post the end result in the form of the Dx wrappers, along with copious notes on design choices that I made.

Until next time…

---

<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 2, 2025, 6:46am UTC](https://discourse.aurelia.io/t/guidance-needed-ive-gotten-aurelia-2-0-templates-through-enhance-to-work-with-devextremes-widget-integration-point/6544/13 "2025-12-02T06:46:54Z")

</div>

> I’m still not too fond of having to work with the container directly in this way. But at least I can encapsulate access into a base class, as I have done in this case.

Maybe the trick is to see it as `viewResources`. Regardless of design choice, there always needs to be something to hold the resources registrations. At the start of v2, we experimented with the consolidation. Later on it was deemed ok, and expensive to change so we left it alone. I think unless there is some good feedback, and direction on what a better API design is, we will likely only be going around if we are to move to another API similar to `viewResources`.

And looking towards to your design notes.
