angularweb-componentcustom-element

Extending native HTML element in Angular 18


I created a node package for a Custom Element that I want to use in Angular and React. Therefor, in this library, I do not want specific Angular of React code.

My custom element is extending a button:

customElements.define('fancy-button', FancyButton, {extends: 'button'});

And this is how I use it:

<button is="fancy-button">Click me</button>  

This all works fine.

Until I try to use my custom element in a Angular component, then it suddenly behaves as a regular button. This answer is using a wrapper div, but I don't want that. Like the answer says: somehow (probably) Angular is first creating the button element, and afterwards is adding the is="fancy-button", which doesn't upgrade the button element to my FancyButton.

I want users of the library able to use the button in their html (<button is="fancy-button">Click me</button>), not force them to use javascript to generate this button.

The answer was in times of Angular 6, maybe there is a solution by now?


Solution

  • The is= and extending existing elements was an early part of the web components spec that was dropped. Some bits of it will work some of the time in some browsers, but you're best off not using it.

    There seem to be a lot of old answers on here telling you to use it, and so AI consistently seems to suggest it, but is= syntax is deprecated, do not use it!

    For security reasons you should not be able to extend any built-in component. Web components can extend other user-defined classes, but not browser-owned ones.

    So...

    You can check for whether your component has loaded with await customElements.whenDefined('fancy-button'), but a more common practice is to use a CSS :not(:defined) pseudo-class to render something for the component that hasn't loaded yet (or that's blocked by JS not loading).

    You can get the JS overhead right down, but ultimately you need that customElements.define. If you want to have a fallback that works outside of JS then you can use the slotted content (like <fancy-button><button>fallback</button></fancy-button>) as unregistered web components just display whatever is inside them.