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.
ParentComponent
@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
ParentService:
@Injectable()
export class ParentService {
selectorDirectives: BehaviorSubject<
SelectorDirective[]
> = new BehaviorSubject([]);
constructor() {}
}
SelectorDirective:
@Directive({
selector: '[appSelector]'
})
export class SelectorDirective {
constructor(public componentRef: Basecomp) {}
}
Note:
- In the constructor we inject the
Basecomp
type, so each element this directive is attached to, need to provide this type.
Basecomp
export class Basecomp {}
Note:
- This is only used as injection token
Now we need to provide the intances of our child-components for our uniform token:
Comp1Component
@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:
- We provide the instance of this component also with the Basecomp-Token. If the selectorDirective is attached to a component which provides this token we can access the component-instance inside of the directive.
Now look back at ngAfterContentInit of ParentComponent:
- We select all ContentChildren which have the SelectorDirective
- We take this Elements an emit them on the Subject in our ParentService
- Also we look for changes of the QueryList, if changes occour we emit them on the Subject in our ParentService
No we can inject ParentService in comp2 and have access to all siblings:
Comp2Component
@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