Repository: angular
Issue: Support for dynamic Angular Elements / Webcomponents (with Renderers)
Labels: feature,area: core,area: elements,feature: under consideration

# 🚀 feature request

### Relevant Package
The functionality relates to the `@angular/platform-browser` package as this is the package which implements `DomRendererFactory2` and `DefaultDomRenderer2`


### Description
Angular Elements and Webcomponents in general represent great solution for various [real world use cases](https://angular-extensions.github.io/elements/#/docs/use-cases#sub-applications) like...
* sub-applications - ability to deploy library  independently which will update all consumers without need to rebuild these consumer SPAs because lib is only referenced by url (as Angular Element bundle)
* microfrontends ( runtime-configurable )

Usual approach to consuming Angular Elements and Web components leaves us with 2 options:
1. eager loading + standard Angular template binding `<some-element [prop]="value">`
2. lazy loading + imperative component creation and props / event binding `document.createElement('some-element');` ...

The library `@angular-extensions/elements` solves this by enabling both **lazy loading** and **standard Angular template binding** `<some-element *axLazyElement="bundle.js" [prop]="value">` and this works with both ViewEngine and IVY...

### Dynamic element use case
In previous section we're committing to element being `<some-element>` by hard-coding it into template of the consumer Angular component. What if we wanted to support loading element configuration at runtime (eg from backend) to enable fully dynamic microfrontends?

As it turns out this works too (at least for the ViewEngine) so it is possible to write `<ax-lazy-element *axLazyElementDynamic="'some-element'; url: 'bundle.js'" [prop]="value"></ax-lazy-element>`.
The bundle will be lazy loaded, and then the element will be rendered as `<some-element>`.
The only thing needed to do that is to override value of the `tagName` in the `TemplateRef` of the `*axLazyElemendDynamic` directive and Angular will render desired element supporting our use case.

Now this does NOT work with IVY since in IVY template is an function. 
Rob @robwormald gave me hint that it should be possible to override `Renderer` (or use custom) to hook into rendering and override element there.

### The Issue
It is possible to achieve that with render BUT:
* currently both `DomRendererFactory2` and `DefaultDomRenderer2` are private so it is NOT possible to extend them and hence I would need to re-implement the whole renderer which sounds pretty excessive and hard to maintain
* PLUS as far as I am aware the render API changed [older NRWL article](https://blog.nrwl.io/experiments-with-angular-renderers-c5f647d4fd9e) so there is no more `RootRenderer` ?

### Current [solution](https://github.com/angular-extensions/elements/blob/master/projects/elements/src/lib/lazy-elements/lazy-element-dynamic/lazy-element-dynamic.directive.ts#L71-L80)
Play around with **live demo** (which serves both lib and demo and rebuilds on changes to any of them) using this one liner `git clone https://github.com/angular-extensions/elements.git elements && cd elements && npm ci && npm start` once it runs, please navigate to `Examples > Dynamic` to see it in action.


```typescript
@Directive({
  selector: '[axLazyElementDynamic]'
})
export class LazyElementDynamicDirective implements OnInit {
  @Input('axLazyElementDynamic') tag: string;
  @Input('axLazyElementDynamicUrl') url: string;

  constructor(
    @Inject(DOCUMENT) private document: Document,
    private renderer: Renderer2,
    private cdr: ChangeDetectorRef,
    private template: TemplateRef<any>,
    private elementsLoaderService: LazyElementsLoaderService
  ) {}

  ngOnInit() {
    this.elementsLoaderService
      .loadElement(this.url)
      .then(() => {
        this.vcr.clear();
        const originalCreateElement = this.renderer.createElement;
        this.renderer.createElement = (name: string, namespace: string) => { // temporary override 
          if (name === 'ax-lazy-element') {
            name = this.tag;
          }
          return this.document.createElement(name);
        };
        this.vcr.createEmbeddedView(this.template);
        this.renderer.createElement = originalCreateElement;
        this.cdr.markForCheck();
      })
  }
}
```
 


### Describe the solution you'd like
Probably being able to easily extend default renderer and being able to register it like we're used to with interceptors or control value accessors.

I would like to give people option (or do it for them behind the scenes) to do
```typescript
@NgModule({
   import: [LazyElementsModule],
  providers: [{ provide: Renderer, useClass: LazyElementsRenderer }]
})
export class AppModule {}

export class LazyElementsRenderer  extends DefaultDomRenderer2  {
  
 constructor(...args) {
     this.super(...args);
  }

  createElement() {
      // override logic and change tag name if necessary
  }
}
```


### Describe alternatives you've considered
I spent quite some time trying to override tag inside of template function without success