Skip to content

Ability to replace the root injector in an angular elements component factory #67168

@eblocha

Description

@eblocha

Which @angular/* package(s) are relevant/related to the feature request?

elements

Description

I would like to be able to destroy the injector used to define a custom element, and replace it with a new instance.

For example, if a host application uses a custom element on one page, it would be useful to be able to completely destroy that custom element's injector (and all created root services) when leaving the page. For large complex apps, this can help keep memory usage low when navigating through the application.

Proposed solution

Injector Factory

I think this could be accomplished by allowing applications to provide an injector factory to createCustomElement. When a new element is created, it can use the factory to get the injector for the component. This would allow an application to destroy an injector when it knows no instances of the custom element are on the page (such as in a single-spa unmount hook). It can then lazily initialize a new injector when appropriate.

export type NgElementConfigStaticInjector = {
  injector: Injector
};

export type NgElementConfig InjectorFactory= {
  injectorFactory: () => Injector
};

export type NgElementConfig = (NgElementConfigStaticInjector | NgElementConfigInjectorFactory) & {
  strategyFactory?: NgElementStrategyFactory;
}

An alternative may be to expose the ComponentNgElementStrategyFactory used internally. This way, applications can reuse the default implementation but provide a different injector than the initial one. This isn't as "clean" of an approach, in my opinion.

Challenge: observedAttributes

The custom elements implementation requires defining a static array of attribute names which trigger lifecycle hooks on the custom element. Angular uses the component inputs for the observed attributes.

Currently, the provided injector is used to compute the inputs defined by the component using the ComponentFactoryResolver in the injector. I am not sure if it would be problematic to continue to use the original list of observed attributes if the injector changes. A new injector could technically define its own component factory which reports different inputs each time, but such a case would be very unusual.

Component inputs shouldn't in theory be dependent on the injector used to create the component.

Alternatives considered

Un-registering custom elements

Since the web platform doesn't provide a way to "un-register" custom elements, it isn't possible to change the ng element constructor assigned to an element tag.

Passing a config object where injector is a getter

This is hacky, and doesn't work because the inner system stores its own reference to the injector to pass it to the NgElementStrategyFactory for each new component instance.

Custom NgElementStrategyFactory

This is not practical to do, because the default implementation is both private (can't be extended or reused), and relies on internal APIs inside Angular to work properly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: elementsIssues related to Angular Elements

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions