Skip to content

[feature] NgComponentOutlet #11168

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?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: commonIssues related to APIs in the @angular/common packagefeatureLabel used to distinguish feature request from other issues

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions