Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting reference of ngComponentOutlet

I'm creating dynamically a component with ngComponentOutlet. Sounds like:

import {Component, NgModule} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'

@Component({
  selector: 'alert-success',
  template: `
    <p>Alert success</p>
  `,
})
export class AlertSuccessComponent {  }

@Component({
  selector: 'alert-danger',
  template: `
    <p>Alert danger</p>
  `,
})
export class AlertDangerComponent {
  test = 'danger...';
}

@Component({
  selector: 'my-app',
  template: `
    <h1>Angular version 4</h1>
    <ng-container *ngComponentOutlet="alert"></ng-container>
    <button (click)="changeComponent()">Change component</button>
  `,
})
export class App {
  alert = AlertSuccessComponent;

  changeComponent() {
    this.alert = AlertDangerComponent;
    alert(this.alert.test);       <-- ???????
  }
}

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ App, AlertSuccessComponent, AlertDangerComponent ],
  entryComponents: [AlertDangerComponent, AlertSuccessComponent],
  bootstrap: [ App ]
})
export class AppModule {}

In changeComponent(), I try (naively I guess) to get the reference of the current component to feed it with data, but it failed :(

Should I have to use ViewContainerRef, how?

like image 990
Stef Avatar asked Nov 08 '22 20:11

Stef


1 Answers

You have to put the component name directly there:

<ng-container *ngComponentOutlet="AlertDangerComponent;
            ngModuleFactory: alertDangerModule;"></ng-container>

I took the liberty to add the Module, is used for when you render a component from a Module different from the current Module.

Also, for using the Module option, you'll need this in your current component:

private alertDangerModule: NgModuleFactory<any>;

constructor(private compiler: Compiler) {
      this.alertDangerModule = compiler.compileModuleSync(AlertDangerModule);
}

If you just want to load 1 component from current Module, this is what you need to do:

<ng-container *ngComponentOutlet="AlertDangerComponent"></ng-container>

NgComponentOutlet

For importing Module: NgModuleFactory

Update (Dynamic):

Create a vector, such as:

import AlertDangerComponent from './path';
import AlertSuccessComponent from './path';

export const MY_ALERTS = {
    'alertDanger': AlertDangerComponent,
    'alertSuccess': AlertSuccessComponent,
};

In your component, you import MY_ALERTS, and you could render as many components as MY_ALERTS has.

Or you could try render it dynamically, by creating a new ng-container (Haven't test this yet).

I'm using this to render components from a huge vector containing component classes with other values such as booleans so I know which component to load each time.

To render a component that is inside this vector you can:

<div *ngFor="let alert of MY_ALERTS | keys">
        <ng-container *ngComponentOutlet="MY_ALERTS[alert];
                 ngModuleFactory: commonAlertsModule;"></ng-container>
</div>

Where keys is just a @Pipe that returns me the keys of an object (instead of the value).

Update (Alternative approach):

I was thinking that maybe you could be interested on this other approach: Use a @Component as a 'Directive'. I'll explain myself:

Declare your Components with a directive like selector:

@Component({
  selector: '[alert-success]',
  template: `
    <p>Alert success</p>
  `,
})
export class AlertSuccessComponent {  }

@Component({
  selector: '[alert-danger]',
  template: `
    <p>Alert danger</p>
  `,
})
export class AlertDangerComponent {
  test = 'danger...';
}

Then, you just call one or the other, depending on occasion:

@Component({
  selector: 'my-app',
  template: `
    <h1>Angular version 4</h1>
    <div *ngIf="alertDanger" alert-danger></div>
    <div *ngIf="alertSuccess" alert-success></div>
    <button (click)="changeComponent()">Change component</button>
  `,
})
export class App {
  alertSuccess = true;
  alertDanger = false;

  changeComponent() {
    this.alertSuccess = !this.alertSuccess;
    this.alertDanger = !this.alertDanger;
  }
}

In my example (not tested though) I initialize the Success Alert. On click, It should set alertSuccess as false and set alertDanger as true.

like image 191
SrAxi Avatar answered Nov 15 '22 12:11

SrAxi