Search code examples
angularng-templateng-content

How to render a part of Component A in Component B


I have a component A and B

<div class="A">
    <div class="A1"></div>
    <div class="A2"></div>
</div>

<div class="B">
    <!--want to display <div class="A1"></div> here-->
</div>

Component B does not contain component A or way around

<componentA></componentA>
<componentB></componentB>

I tried to wrap <div class="A1"></div> in to ng-template and then in componentB I was querying for that, but it is always undefined:

@ViewChild('templ') templ: TemplateRef<any>;

Then I realized that @ViewChild is to query component children, so how would you query and render <ng-template> it it is not child component?


Solution

  • Have a look in to this awesome sideNav sample by Alex Rickabaugh, that demoing exactly what you need.

    Based on that you need to define an ng-container in your ComponentA. Then you need to mark the piece of your ComponentB with a custom directive and there is a service that connecting ComponentA and ComponentB:

    <div class="A1"><div>
    <div class="A2" *leftNav >
      <h3>Content</h3>
    </div>
    

    Directive:

    @Directive({
      selector: '[leftNav]',
    })
    export class LeftNavDirective implements OnInit {
      constructor(private leftNav: LeftNav, private ref: TemplateRef<any>) {}
    
      ngOnInit(): void {
        this.leftNav.setContents(this.ref);
      }
    }
    

    Service:

    @Injectable()
    export class LeftNav {
      private _state = new BehaviorSubject<TemplateRef<any>|null>(null);
      readonly contents = this._state.asObservable();
    
      setContents(ref: TemplateRef<any>): void {
        this._state.next(ref);
      }
    
      clearContents(): void {
        this._state.next(null);
      }
    }
    

    Finally in your ComponentB you have to subscribe for your <div class="A1"> ... <div> thing on ngAfterViewInit:

    ngAfterViewInit(): void {
        this
          .leftNav
          .contents
          .subscribe(ref => {
            if (this._current !== null) {
              this._current.destroy();
              this._current = null;
            }
            if (ref === null) {
              return;
            }
            this._current = this.vcr.createEmbeddedView(ref);
        });
      }
    

    P.S there is a video and slides available on that topic.