Render app inside a web component

I have built an aurelia v2 app and would like to add it to an existing website.

I have gotten this to work by copying the created dist folder into the root of the website so it would look something like this:

<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <script defer="defer" src="dist/entry.6d5787857fb220b5df4b.bundle.js"></script>
</head>
<body>
  
  <div style="background: lightblue; height:300px; display: flex; align-items:center;">
    THIS IS THE HEADER
  </div>
  
  <my-app></my-app>

  <div style="background: lightblue; height:300px; display: flex; align-items:center;">
    THIS IS THE FOOTER
  </div>
</body>
</html>

Everything seems to be working and the app is displaying and working correctly. When this script entry.6d5787857fb220b5df4b.bundle.js is loaded, then it looks for a my-app tag and bootstraps the aurelia app into that.

Now, I assuming that this is not the best way to do this. Has anyone tried something like this?

Maybe the aurelia app can be rendered inside a web component, which can the be added to an external webpage or every nested within another application that was built using react or angular.

Any pointers or tips would be much appreciated.

Found some more info here

@bigopon wrote:

Yes, there’ at least two ways:

  1. render an Aurelia app. All it requires is a host element and a root component. This means in the Angular app, you will get some element reference, and instantiate an Aurelia app with this element as host.
  2. As you notice, if you want to simplify the process of creating an element, any do not want to instantiate Aurelia apps in many places, you can use native web component to do this.

So I guess I am already doing number 1.

Hi @ivan! I think what you are doing is also perfectly alright. If you want to render from a WebComponent, then I think it might not be that different. IMO the process is roughly as follows:

  • Create a “root” element to host the aurelia app.
  • Attach that root to the DOM (or for WC might ShadowDOM).
  • Use this element to host the aurelia app as usual.
1 Like

So I got a custom web component to work by adding this to the main.js file of my project.

import { Aurelia, DialogDefaultConfiguration } from 'aurelia'
import { MyApp } from './my-app'

class MyComponent extends HTMLElement {
  constructor() {
    super()
  }

  connectedCallback() {    
    Aurelia
      .register(DialogDefaultConfiguration)
      .app({
        component: MyApp,
        host: this
      })
      .start()
  }
}

customElements.define('my-component', MyComponent)

With a couple of small changes to the webpack config, this now allows me to publish the build to npm, import it into another project and use the custom element.

In the code example above, I am not using a shadow dom. I did some initial experiments, but could not get the styles to load correctly. Seems that Aurelia always adds the styles to the <head> tag of the document body.

Is there a way to add the css directly to the component element?

1 Like

Is there a way to add the css directly to the component element?

I think the convention is to add the styles in head. However, this is more or less a bundling aspect as far as I understand. @huochunpeng is there a way to customize it?

Although, if you are trying to restrict the styles to your individual custom element, you may want to use CSS modules that renames your CSS selectors uniquely. With that even if the styles are added globally, it should not pose any conflicts.

1 Like

au2 handles css in ShadowDOM or CSS-Modules mode, but the default global HEAD css injection is not handled by au2, but bundler (webpack in this case).

Try customise webpack’s style-loader.

1 Like

That’s great. Thank you @Sayan751 & @huochunpeng.

I have another question :sweat_smile: Say I have defined a couple of custom elements in my app. E.g. custom-element-1 and custom-element-2.

The template for custom-element-1 looks like this:

<import from='./custom-element-2'></import>

<custom-element-2></custom-element-2>

...

In my main.js I define a web component like this:

...
.register(AppTask.beforeCreate(IWcElementRegistry, registry => {
    registry.define('web-component-custom-element-1', CustomElement1)
  }))
...

Now whenever I include <web-component-custom-element-1> in my html, custom-element-1 will be rendered, however custom-element-2 does not get rendered.

Do I need to use au-compose here or something similar?

hehe, so it seems that by registering custom-element-2 as a global, it now renders :upside_down_face:.

...
.register(CustomElement1)
...

Thanks again for all the help.

1 Like