Skip to content

[feature] NgComponentOutlet #11168

@shlomiassaf

Description

@shlomiassaf

I'm submitting a ... (check one with "x")

[ ] bug report => search github for a similar issue or PR before submitting
[ x ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior
None

Expected/desired behavior
A directive that is able to create components dynamically, the same way as NgTemplateOutlet created embedded view dynamically.

Pros

  • Remove the boilerplate in a component. Developer does not need to query (ViewChild) and manually create the component. (ComponentFactoryResolver, ViewContainerRef)
  • Expressed in the template.
  • Simple
  • Aligned with NgTemplateOutlet, same idea same concept

Cons

  • Host attribute bindings are not expressed in a template. Not much of a con, as its expected and ComponentRef can be used to set values on the instance...
  • Getting a reference to the returned ComponentRef is via callback only.
  • more ?

Directive

@Directive({
  selector: '[swapCmp]'
})
export class SwapComponentDirective {
  private component: any;

  constructor(private cfr: ComponentFactoryResolver,
              private vcRef: ViewContainerRef,
              private tRef: TemplateRef<Object>) {
  }

  @Input() swapCmpBindings: ResolvedReflectiveProvider[];
  @Input() swapCmpInjector: Injector;
  @Input() swapCmpProjectables: any[][];

  @Output() onCreate: EventEmitter<ComponentRef<any>> = new EventEmitter<ComponentRef<any>>(false);

  @Input() set swapCmp(component: any) {
    this.component = component;
    this.vcRef.clear();
    if (this.component) {
      let injector = this.swapCmpInjector || this.vcRef.parentInjector;

      if (Array.isArray(this.swapCmpBindings) && this.swapCmpBindings.length > 0) {
        injector = ReflectiveInjector.fromResolvedProviders(this.swapCmpBindings, injector);
      }

      const cmpRef = this.vcRef.createComponent(
        this.cfr.resolveComponentFactory(component),
        this.vcRef.length,
        injector,
        this.swapCmpProjectables
      );

      cmpRef.changeDetectorRef.detectChanges();

      this.onCreate.emit(cmpRef);
    }
  }
}
 <template [swapCmp]="ctx.component" [swapCmpBindings]="ctx.bindings"   [swapCmpInjector]="ctx.injector" [swapCmpProjectables]="ctx.projectableNodes"></template>

The name here is SwapComponent but it just as an example, took it from my code base

  • A Developer provides a component, changing the component will re-create.
  • Injector is optional, if not supplied the current injector is used.
  • Bindings are used to allow additional providers to the injector (something like viewProviders)
  • In case ng-content is needed the developer can supply projectableNodes.

What do you think?

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: commonIssues related to APIs in the @angular/common packagefeatureIssue that requests a new feature

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions