templating icon indicating copy to clipboard operation
templating copied to clipboard

Templating creates unwanted custom elements

Open jods4 opened this issue 7 years ago • 15 comments

I am editing this issue, the original text is below.

I originally thought that the root of the problem was Aurelia creating custom elements from exports without convention nor decorator. But from comments below it was clarified that this is actually supported in some scenarios.

With this in mind, I now think that the root of the problem is that Aurelia should not register a local custom element when instantiating a dialog like so:

import { MyDialog } from 'dialogs/whatever';
dialogService.open({ viewModel: MyDialog });

This code should not result in the registration of a <my-dialog> custom element.

It is mostly harmless, but if your exported name clashes with other tags it's breaking the app.


From aurelia/dialog#283: When loading a component (e.g. a view model for a dialog, a page to be loaded in the router, etc.), aurelia-templating falls back to creating a custom element based on the exported component name.

I think the logic for that is here: https://github.com/aurelia/templating/blob/7693dbd65e59e428e4052922920145b76240f3cf/src/module-analyzer.js#L253

Now consider what happens if I choose to name my ViewModel export class A { }. When used by a dialog or router, it will create a <a> custom element, which is of course unwanted and will trigger bugs if there are links in the template.

Expected behavior: Only classes decorated with @customElement or following a convention (by default ending in CustomElement) should create custom elements.

jods4 avatar May 03 '17 16:05 jods4

CC: @StrahilKazlachev, @EisenbergEffect

jods4 avatar May 03 '17 16:05 jods4

We aren't changing this. This convention-based approach is fundamental to Aurelia and is one of the reasons why people choose Aurelia.

EisenbergEffect avatar May 03 '17 16:05 EisenbergEffect

If you want to create a feature to configure Aurelia to opt-out of this, that's an option. But we can't change this all up. That would be devastatingly destructive.

EisenbergEffect avatar May 03 '17 16:05 EisenbergEffect

@EisenbergEffect I am not sure you understood the bug correctly, or maybe I was unaware of an Aurelia option for a long time!

From the official documentation

Aurelia will take the JavaScript class name, strip CustomElement from the end, and convert it from InitCaps to dash-case for the custom element's name. [...] It is also possible to explicitly name your custom element by using the customElement decorator

I don't see a mention that classes with random names are converted to custom elements.

This is what happened in aurelia/dialog#283: a class that was not meant to be a custom element, named just "a" at runtime, was interpreted as a <a> custom element resource.

jods4 avatar May 03 '17 17:05 jods4

Re-posting here from https://github.com/aurelia/dialog/issues/283#issuecomment-306937846

Since it seems that this would be quite a breaking change, since the behavior is a "supported" (even if slightly discouraged) scenario -- is there a much easier (low risk) fix here?

Without breaking things, it doesn't seem like you can reasonably stop people from exporting a module that matches a dom thing -- but honestly if you do that -- you are on your own. But because in this case it isn't the user naming it, it is webpack changing the names, could the template stuff just be protected from using single-letter names? Which I would think would cover the majority (if not all) of these issues caused by webpack name shortening. OR is there way to to control the webpack name shortening to include some prefix (I have no idea of the allowable characters in a module name -- but say $) so that they would never conflict with a dom element?

eamodio avatar Jun 07 '17 22:06 eamodio

User @eamodio made some good points in the original aurelia-dialog issue.

Apparently, using custom elements with no decorator and no naming convention is a thing. It was promoted in several places, including in the current docs:

This means that, if you wish, you may ignore the Aurelia naming convention for your custom elements. In the example above, we could have simply named the class SecretMessage. The custom element would still be named secret-message. Given this capability, it might be considered wise to utilize Aurelia's naming convention for custom elements or use the customElement decorator to be explicit when creating a component that is only meant to be used as a custom element and not as a standalone page.

So it's discouraged, but supported. I'm afraid making the CustomElement mandatory will break too much code to be doable.

Yet I still believe we have an important bug that more and more users of aurelia-dialog will hit when switching to Webpack. It's just that the fix might be more complicated.

As a reminder, the bug arises from the fact that aurelia-dialog instantiate a ViewModel without going through our plugin special dependencies, like so:

import { MyDialog } from 'dialogs/whatever';
dialogService.open({ viewModel: MyDialog });

Somehow, MyDialog -- or whatever the name -- ends up being a custom element, which is wrong.

Maybe an alternative fix is to only allow convention-less custom elements when going through <require>?

jods4 avatar Jun 07 '17 22:06 jods4

@jods4 but isn't it worse than that?

Somehow, MyDialog -- or whatever the name -- ends up being a custom element, which is wrong.

Because webpack shortens the name of MyDialog to be something like a? Or am I getting that wrong?

eamodio avatar Jun 07 '17 22:06 eamodio

@eamodio For me the core bug that has been there for a long time is that I don't expect a my-dialog custom element to be created in this situation.

The situation is indeed worse now that tools like Webpack rename the imports, which can create custom elements for <a>, <b>, <dd>...

But fundamentally, I don't think renaming is the cause of the issue. It just makes it worse/more visible.

jods4 avatar Jun 07 '17 22:06 jods4

Yeah, for me that makes it dramatically worse -- because it isn't self inflicted. And doubly so, because the errors it causes are so far removed from the actual problem -- we spun for the better part of a day trying to figure out what was going on (before we found the github issue about it) during our (ongoing) conversion from systemjs/jspm to webpack.

eamodio avatar Jun 07 '17 22:06 eamodio

This seems quite an urgent issue since WebPack is now the default in Aurelia/Cli. I ran into the same problem twice this week and lost a couple of hours :-(

Scapal avatar Jun 20 '18 19:06 Scapal

Ok, thanks to @bigopon, the problem goes away by using PLATFORM.moduleName in the DialogService.open() viewModel parameter.

Scapal avatar Jun 21 '18 09:06 Scapal

This workaround for DialogService.open(), while it does work and I'm grateful for that, is outright horrible. I have to make a massive app wide refactor that can't be easily automated with sed/awk and throws away type safety, forcing me to validate that every single dialog in my app still works. All thanks to a 4 year old bug that has had no effort put in. Sigh 💩

sflanker avatar Jun 19 '21 03:06 sflanker

@sflanker tou can refactor eitber in the user of the view model, or the view model itself by giving it an explicit name via a @customElement()

bigopon avatar Jun 19 '21 03:06 bigopon

@bigopon So if I simply explicitly decorate every dialog viewModel type, I can safely continue to references these view models using the type name imported using an ES style import? That is good news 🙏

Just to clarify to be sure I understand the workaround:

// view that uses a dialog
import MyDialog from './my-dialog';

export default class MyPage {
  // ...

  public void showMyDialog() {
    this._dialogService.open({
      viewModel: MyDialog
    });
  }
}

// my-dialog.ts

@customElement('my-dailog') // <<--- this is the critical thing that prevents the stack overflow, need even though I never reference this with <my-dialog></my-dialog>
export default class MyDialog {
}

sflanker avatar Jun 21 '21 21:06 sflanker

@sflanker yes thats exactly it

bigopon avatar Jun 22 '21 11:06 bigopon