Search code examples
angularangular2-templateangular-directiveangular-ivy

Angular get viewContainerRef of ViewRef


i have a definition of templates in my angular app:

<div class="wrapper" grid [data]="data">
  <div class="cell" *cellDef="let cell">{{cell.id}}</col>
  <div class="row" *rowDef="let row"></div>
</div>

Right now I create "rows" in a directive based on some data injected to wrapper. I create for example 10 rows by creating embeddedViews with templateRef and viewContainerRef of rowDef.

Same for cells, but I want to render cells inside of the rows viewRef. My problem right now is, that I am not able to get the viewContainerRef of the embedded TR Element created by rowDef.

Attaching another directive (non-strucural) let me inject the viewContainerRef, but it only gave me the viewContainerRef where the tr is rendered. How can I get the viewContainerRef inside the tr element to attach the cells to?

<div class="row" *rowDef="let row"  non-structural-directive></div>

@Directive({selector: '[non-structural-directive]'})
export class NonStructuralDirective {
  // points to "wrapper" instead of "row" so it isn't helpful at all
  constructor(vc:ViewContainerRef)
}

Solution

  • For those interested into the solution...

    In the end it was really simple and already solved by cdk-table. So it was not my solution neither my idea. (By the way, it is simply brilliant how it was designed)

    I've created a component which uses my non-structural-directive as selector. Also I used an ng-container with another directive where I can get the viewContainerRef when it gets rendered.

    /**
     * this is the missing piece. You can use components 
     * to select directives and html tags and add html to it.
     * by adding ng-container with another directive we are 
     * now able to reference viewContainerRef of the inner 
     * tr element
     */
    @Component({
      selector: 'non-structural-directive, tr[non-structural-directive]',
      template: '<ng-container outlet>/<ng-container>'
    })
    export class StructuralComponent {}
    
    
    /**
     * will be created after tr was embedded 
     * provides handle for referencing viewContainerRef inside of tr element
     */
    @Directive({
      selector: 'outlet'
    })
    export class OutletDirective {
      static lastCreatedViewContainerRef:ViewContainerRef;
      constructor(vc:ViewContainerRef) {
        OutletDirective.lastCreatedViewContainerRef = vc;
      }
    }
    

    For the Outlet Directive it is important to provide a static variable to store the latest created viewContainerRef, otherwise I will not know where it belongs to. If someone needs further explaination, feel free to respond, I will extend my answer.