I have a base component (MyBaseComponent) receiving data from it's invoker (App).
I want to know if it's possible for my base component to be told by it's invoker what 'display' component it should use -- MyDisplayComponent or MyOtherDisplayComponent.
I.e., I want to do something along the lines of:
<my-base [data]="names">
<my-other-display></my-other-display>
</my-base>
and in a different location, something like:
<my-base [data]="names">
<my-display></my-display>
</my-base>
But I'm unable to figure out how to do this. I've tried different use of <ng-content>
, but I don't think it provides for this level of flexibility. Additionally ComponentFactoryResolver
sounds like it could work, but again I'm not sure how it could work in an iterative fashion, as well as pass data to the child controls.
Here's a working Plunker, with the code outlined below, which hopefully conveys what I'm attempting.
app.ts
import {Component, NgModule, VERSION} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import {MyBaseComponent} from './my-base.component'
import {MyDisplayComponent} from './my-display.component'
import {MyOtherDisplayComponent} from './my-other-display.component'
@Component({
selector: 'my-app',
template: `
<div>
<my-base [data]="names">
<!-- Is it possible to specify the 'display component' type to use here? -->
</my-base>
</div>
`,
})
export class App {
names: string[] = ["bill", "bob", "ben"];
constructor() {
}
}
@NgModule({
imports: [ BrowserModule ],
declarations: [ App, MyBaseComponent, MyDisplayComponent, MyOtherDisplayComponent ],
bootstrap: [ App ]
})
export class AppModule {}
my-base.component.ts
import {Component, Input} from '@angular/core'
@Component({
selector: 'my-base',
template: `
<div *ngFor="let datum of data">
<!-- Instead of specifying the display components here,
how can I specify the components type to use for display
at the 'app.ts' level? -->
<!--<my-display [value]="datum"></my-display>-->
<my-other-display [value]="datum"></my-other-display>
</div>
`,
})
export class MyBaseComponent {
@Input()
data: string[];
constructor() {
}
}
my-display.component.ts
import {Component, Input} from '@angular/core'
@Component({
selector: 'my-display',
template: `
<div>{{value}}</div>
`,
})
export class MyDisplayComponent {
@Input()
value:string;
constructor() {
}
}
my-other-display.component.ts
//our root app component
import {Component, Input} from '@angular/core'
@Component({
selector: 'my-other-display',
template: `
<div>{{value}}! {{value}}! {{value}}!</div>
`,
})
export class MyOtherDisplayComponent {
@Input()
value:string;
constructor() {
}
}
Again, the big 'ask' here is whether it's possible to specify the display
components for use at the app.ts (invoker) level so that the my-base.component.ts file can be ignorant of the specific implementation used in a given situation.
It may be that the my-base.component.ts file needs to pass a class name or a type to use that adheres to some kind of interface...?
Thanks in advance for any help.
You may be able to use ngComponentOutlet
to choose the component and probably ngIf
or switch for which to show, I'm not sure what your going for.
Plunker
<ng-container *ngComponentOutlet="MyDisplayComponent"></ng-container>
// in module
entryComponents : [ MyDisplayComponent, MyOtherDisplayComponent],
1) You can try using ngTemplateOutlet
directive like
my-base.component.ts
@Component({
selector: 'my-base',
template: `
<div *ngFor="let datum of data">
<ng-container *ngTemplateOutlet="tmpl; context: { $implicit: datum }"></ng-container>
</div>
`,
})
export class MyBaseComponent {
@ContentChild(TemplateRef) tmpl: TemplateRef<any>;
@Input() data: string[];
}
app.component.ts
@Component({
selector: 'my-app',
template: `
<my-base [data]="names">
<ng-template let-item>
<my-other-display [value]="item"></my-other-display>
</ng-template>
</my-base>
<my-base [data]="names">
<ng-template let-item>
<my-display [value]="item"></my-display>
</ng-template>
</my-base>
`,
})
export class App {
names: string[] = ["bill", "bob", "ben"];
}
Forked Plunker
2) Using ngForTemplate
. Almost the same approach with only one difference in
my-base.component.ts
@Component({
selector: 'my-base',
template: `<div *ngFor="let datum of data; template: tmpl"></div>`
})
export class MyBaseComponent {
@ContentChild(TemplateRef) tmpl: TemplateRef<any>;
@Input() data: string[];
}
Forked Plunker ngForTemplate
instead of ng-content you can simply solve using if and else.
base component will pass true or false value to render
app.ts
<my-base [data]="names" [isValid]=false>
MyBaseComponent.ts
<div *ngIf="isValid;then content else other_content"></div>
<ng-template #content> <div *ngFor="let datum of data">
<my-display [value]="datum"></my-display>
</div></ng-template>
<ng-template #other_content> <div *ngFor="let datum of data">
<my-other-display [value]="datum"></my-other-display>
</div></ng-template>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With