question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Angular Elements should preserve/forward component methods

See original GitHub issue

I’m submitting a…

  • Regression (a behavior that used to work and stopped working in a new release)
  • Bug report
  • Feature request
  • Documentation issue or request
  • Support request

Partially considered a bug due to developer expectations, partially a feature request because it might not be “fixable” in that sense.

Current behavior

When creating Custom Elements from Angular Components using Angular Elements APIs, the resulting elements do not preserve the Angular component methods on the Custom elements.

For example giving a component like this:

@Component(...)
export class CarComponent {
  drive() {...}
}

does not make drive() available on the resulting custom element:

const car = document.querySeletor('car-thing');

car.drive(); // Errors out because `drive()` doesn't exist on <car-thing>

This is because an Angular component doesn’t actually “become” the Custom Element but is rather preserved “inside” the Custom Element and readable via componentRef. To make the code above work we’d need to call

car.componentRef.instance.drive();

Expected behavior

As Angular Elements are being promoted as Angular Components packaged as Custom Elements, I’d expect Angular Elements to have the same characteristics (Custom Elements come with their attributes, properties, methods and events).

In other words, I’d expect this to work:

car.drive(); // Angular takes care of preserving or forward API methods

Some useful notes and ideas on this

I’ve talked to @gkalpak about this already and one of his ideas was to maybe come up with a new decorator that tells Angular to preserve the decorated method on the resulting host element.

Another idea would be to declare in the registration process which methods should be preserved/forwarded. This could look like this:

registerAsCustomElements([{component: CarComponent, methods: ['drive']}], ...)

The reason there might be an additional API for this needed is because we can’t just preserve/forward all of the components methods. Some methods may not be intended to be available on the resulting Angular Element (e.g. lifecycle hooks etc.)

What is the motivation / use case for changing the behavior?

I think it’d be great if consumers of Angular Elements don’t have to worry about Angular internals and can in fact use any Angular Element exactly the same way they’d use any other DOM element. Right now, to call component methods, consumers of Angular Elements have to know that those are populated on componentRef.instance, which doesn’t seem to be very convenient nor what one would expect.

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:28
  • Comments:23 (7 by maintainers)

github_iconTop GitHub Comments

16reactions
wolfe-lienhardtcommented, Dec 27, 2019

FWIW:

Here is a different approach that I used to work around this issue, I found it more palatable than the @Input solution as it feels a bit less like it’s abusing side effects of Angular’s design.

Using the car example from the OP:

@Component(...)
export class CarComponent {
  drive() {...}
}

I manually ‘exposed’ the functions on the native element in the constructor like so:

@Component(...)
export class CarComponent {
  constructor(private host: ElementRef) {
    this.host.nativeElement.drive = this.drive.bind(this);
  }

  drive() {...}
}

This allowed me to then call the function without issue:

car.drive(); // successful vroom vroom

I like this approach as it feels a bit more ‘intentional’ and self documenting by declaring the ‘public’ methods in the constructor upfront. Additionally if you wanted you could wrap them in more defensive checks to make sure you’re not clobbering functions with the same name that are already present on the nativeElement. This could provide a decent amount of backwards compat if you ever bumped up to a version of angular that resolved this issue.

I’m not an angular wizard though so if someone see’s an issue with this approach let me know!

5reactions
kukjevovcommented, Oct 28, 2018

Any news here ? I see it was put to backlog 😃.

@aukris BTW you can use methods using Input, but you have to write it one of the following ways:

@Input()
public methodName = (someParameter: string) =>
{
  console.log(this); //it will work
}

or

@Input()
public methodName = function(this: TypeOfComponentClass, someParameter: string)
{
  console.log(this); //it will work
}.bind(this)
Read more comments on GitHub >

github_iconTop Results From Across the Web

Angular elements overview
Angular elements are Angular components packaged as custom elements (also called Web Components), a web standard for defining new HTML elements in a ......
Read more >
Using Angular Elements — Why and How? — Part 1 | by Rajat S
But first, let's take a look at why you should use Angular Elements. Tip: Building with components? Use Bit to organize share and...
Read more >
Understanding the Magic Behind Angular Elements
define() API method, passing it the element tag name and the transformed Web Component. Now we can use it like any other HTML...
Read more >
Angular Elements — A Practical Introduction To Web ...
By using Angular Elements you can package Angular components as custom ... This event handler method will be executed each time the user ......
Read more >
Building and consuming Angular Elements as Web Components
In this case the runtime and other dependencies will be built as so-called externals. Content image. Each approach has its advantages and ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found