I am writing a common angular library component, where the consumers can pass on optional content/template/component through angular content projection mechanism. Below are some example classes to explain better.
The hierarchy tree:
consuming-component - projects component-to-project ----->
common-container - forwards projected content to it's child ------>
common-item - projected content renders here.
Common Library:
component-to-project.component.ts - this is the component that will finally render inside common-item and is projected from the consuming component.
@Component({
selector: 'component-to-project',
template: '<p>Projected Successfully!</p>',
})
export class ComponentToProjectComponent { … }
common-container.component.ts - This is the intermediate component that passes on the projected content to common-item.
@Component({
selector: 'common-container',
templateUrl: './common-container.component.html',
styleUrls: ['./common-container.component.scss'],
})
export class CommonContainerComponent {
@ContentChild(TemplateRef) projectedTemplateRef!: TemplateRef<any>;
(…)
}
common-container.component.html
...
<div>
<common-item>
<ng-template let-context>
<ng-container [ngTemplateOutlet]="projectedTemplateRef" [ngTemplateOutletContext]="{$implicit: context}">
</ng-container>
</ng-template>
</common-item>
</div>
(…)
common-item.component.ts - This is where the projected content actually renders.
@Component({
selector: 'common-item',
templateUrl: './common-item.component.html',
styleUrls: ['./common-item.component.scss'],
})
export class CommonItemComponent {
@ContentChild(TemplateRef) projectedTemplateRef!: TemplateRef<any>;
templateContext: MyContextData = { … };
ngAfterContentInit() {
// This is where I need the reference to the component passed through content projection.
projectedComponent: ComponentToProject = // get component
}
(…)
}
common-item.component.html
(…)
<div>
<ng-container [ngTemplateOutlet]="projectedTemplateRef" [ngTemplateOutletContext]="{$implicit: templateContext}">
</ng-container>
</div>
(…)
Consuming Application (Common Library is added as dependency)
consuming-app.component.html
(…)
<common-container>
<ng-template let-context>
<component-to-project (data)="context.data"></component-to-project>
</ng-template>
</common-container>
(…)
I want the reference to the component-to-project component inside common-item component if this is what is being projected from consuming app.
I have tried few different approaches to get the projected component reference in the common-child component but got undefined in all cases.
Added a reference (<component-to-project #myComponent>) to the projected component and resolve it using @ContentChild in common-item component
@ContentChild('myComponent', { descendants: true }) projectedComponent?: ComponentToProjectComponent;
Resolve it using component class as selector of @ContentChild in common-item component
@ContentChild(ComponentToProjectComponent, { descendants: true }) projectedComponent?: ComponentToProjectComponent;
Define an injection token for the component and use that injection token in @ContentChild to resolve.
- component to project declaration module providers array
{ provide: TokenClass, useExisting: forwardRef( () => ComponentToProjectComponent).
- common-child
@ContentChild(TokenClass) projectedComponent?: ComponentToProjectComponent;
However, none of the above approach worked.