I am projecting two components.
<app-form-field>
<component-one/>
<component-two/>
</app-form-field>
So if i want to know for example if component one is projected inside app-form-field
i will do:
@ContentChild(ComponentOne) componentOne;
ngAfterContentInit() {
if(this.componentOne) {
alert('it is projected');
} else {
alert('it is NOT projected');
}
}
but i need to check inside my component-two
if for example component-one is projected
i need to check somehow if component-two has sibling content projection - component-one
.
How can i do that in angular ?
It is possible to check if there is a sibling-component projected, but the way to go might be hacky.
First of all we need the component that projects the "child"-components.
@Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css'],
providers: [ParentService]
})
export class ParentComponent implements AfterContentInit {
@ContentChildren(SelectorDirective) public refs: QueryList<SelectorDirective>;
constructor(private p: ParentService) {}
ngAfterContentInit(): void {
this.p.selectorDirectives.next(this.refs.toArray());
this.refs.changes.subscribe(x => {
this.p.selectorDirectives.next(this.refs.toArray());
});
}
ngOnInit() {}
}
Note:
We inject a Service ParentService
on component level, so child and projected components can receive this instance.
For components of differents types we need a uniform Selector (SelectorDirective
) to pass to @ContentChildren
@Injectable()
export class ParentService {
selectorDirectives: BehaviorSubject<
SelectorDirective[]
> = new BehaviorSubject([]);
constructor() {}
}
@Directive({
selector: '[appSelector]'
})
export class SelectorDirective {
constructor(public componentRef: Basecomp) {}
}
Note:
Basecomp
type, so each element this directive is attached to, need to provide this type.export class Basecomp {}
Note:
Now we need to provide the intances of our child-components for our uniform token:
@Component({
selector: 'app-comp1',
templateUrl: './comp1.component.html',
styleUrls: ['./comp1.component.css'],
providers: [
{ provide: Basecomp, useExisting: forwardRef(() => Comp1Component) }
]
})
export class Comp1Component implements OnInit {
constructor() {}
ngOnInit() {}
}
Note:
Now look back at ngAfterContentInit of ParentComponent:
No we can inject ParentService in comp2 and have access to all siblings:
@Component({
selector: 'app-comp2',
templateUrl: './comp2.component.html',
styleUrls: ['./comp2.component.css'],
providers: [
{ provide: Basecomp, useExisting: forwardRef(() => Comp2Component) }
]
})
export class Comp2Component implements OnInit {
constructor(private p: ParentService) {}
c1 = false;
ngOnInit() {
this.p.selectorDirectives.pipe().subscribe(x => {
this.c1 = !!x.find(a => {
return a.componentRef instanceof Comp1Component;
});
});
}
}
Working Example: https://stackblitz.com/edit/angular-ivy-t3qes6?file=src/app/comp2/comp2.component.ts