Au2 - confusion around "enhance"

I’ve been reading the documentation for enhance() (Enhance | The Aurelia 2 Docs), and I’m confused about what the component parameter actually does.

I assumed that if I specify a component constructor, then similarly to how .app() works, Aurelia would mount the component inside host. But that is not what happens.

What actually happens is that the specified component’s lifecycle methods all trigger (I put console.log inside attaching() to try and realize what is happening), but nothing is actually added to the DOM.

So the only way I actually got enhance to work is if I registered a component using DI, and then enhanced a parent element that had a child element with the same tagName as the registered component.

So if this is the way I have to use enhance, what is the point of the component parameter?

I believe the confusion here is when you use enhance to enhance custom elements, the component property is the root controller. Think of it as similar to how <my-app> is the root component in a default Aurelia 2 app scaffolded.

So while you think passing in your component registered via DI to component is what renders it, that’s actually the root component for the host property.

I have created an example here to highlight this: aurelia-enhance-demo - StackBlitz

In index.html you will see this HTML:

    <my-app></my-app>
    <div id="enhance-me"><my-component name.bind></my-component></div>

Notice on my-component I have a bindable called name? Yet when you run the code linked, it shows Aurelia as a value despite the fact my-component.ts doesn’t have a default value (it’s an empty string):

import { bindable } from 'aurelia';

export class MyComponent {
  @bindable name = '';
}

So, where does it come from? It comes from our host root component.

In my-app.ts I have the following:

  attached() {
    const element = document.getElementById('enhance-me');

    this.au.enhance({
      host: element,
      component: MyHost,
    });
  }

In main.ts I already registered my-component, but you’ll notice I am passing MyHost instead.

Going into my-host and you’ll see where the name value comes from:

import { customElement } from 'aurelia';

@customElement({
  name: 'my-host',
  template: null,
})
export class MyHost {
  name = 'Aurelia';
}

So, this becomes the parent for all enhanced children. But, it’s worth noting (as the docs correctly show), you don’t need to pass any root component, you can just specify an empty object {} to the component and it will enhance just fine.

  attached() {
    const element = document.getElementById('enhance-me');

    this.au.enhance({
      host: element,
      component:{},
    });
  }

Provided your custom element being enhanced is globally registered in DI, it’ll still render.

For non-custom element enhancement, the component becomes the view-model.

So in your my-app.ts you could do something like this as an example:

import { IAurelia, resolve } from 'aurelia';

export class MyApp {
  private au = resolve(IAurelia);

  attached() {
    const element = document.getElementById('enhance-me');
    element.innerHTML = '<ul><li repeat.for="fruit of fuits">${fruit}</li></ul>';

    this.au.enhance({
      host: element,
      component:{
          fruits: ['Apple', 'Mango', 'Banana', 'Grapes']
      },
    });
  }
}

And if you ran that, you would see four bullet points with our fruit being rendered.

3 Likes

That makes sense, thanks for the clarification.

No worries @Haltesh

We will have a video coming out shortly about working with enhance, because the confusion you encountered is something that others could easily encounter too.

2 Likes

@dwaynecharrington Thanks for the example, unfortunalty I don’t quite get it.

Here is what I am trying to do:

I have a component. Let’s call it my-map, in this component there is this event listener, so something like this:

onClick() {
  const popup = new Popup() // thirdparty library
    .setHTML('<div repeat.for="item of items">${item}</div>')

  this.aurelia.enhance({
    host: popup._content,
    component: { items: [1, 2, 3] },
  })  
}

This works, the list is rendered. But I would like to render a custom component instead.

so

onClick() {
  const popup = new Popup() // thirdparty library
    .setHTML('<my-component></my-component>')

  this.aurelia.enhance({
    host: popup._content,
    component:{},
  })  
}

Nothing is rendered here.

Any thoughts? Thanks.

Ah nevermind, just forgot to register my-component globally :sob: