angularangular-content-projection

How can i see if there is some sibling projected content?


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 ?


Solution

  • 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:

    ParentService:

    @Injectable()
    export class ParentService {
      selectorDirectives: BehaviorSubject<
        SelectorDirective[]
      > = new BehaviorSubject([]);
    
      constructor() {}
    }
    

    SelectorDirective:

    @Directive({
      selector: '[appSelector]'
    })
    export class SelectorDirective {
      constructor(public componentRef: Basecomp) {}
    }
    

    Note:

    Basecomp

    export class Basecomp {}
    

    Note:

    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:

    Now look back at ngAfterContentInit of ParentComponent:

    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