Angular ViewContainerRef TemplateRef ElementRef

Angular: ViewContainerRef, ElementRef, TemplateRef, ComponentRef

Basic introduction to these four concepts

ViewContainerRef

definition from angular Doc: Represents a container where one or more views can be attached to a component, But, this sentense sounds a little strange or meaningless.
keep these points in mind

  • any Dom element can be used as a view Container.
  • a container is the place where one or more views can be attached to.
  • In angular, every time the view is append as the last in the view container.

In conclusion, viewContainer is used to host componentView or embeddedView.

How to get access to ViewContainerRef?

  • Through ViewChild, we can get access to ng-container or template variables in html template. we can also get injected from constructor. Let's see the example bellow. remember to add {read: ViewContainerRef} in viewChild
@Component({
    selector: 'sample',
    template: `
    <!-- This is component level viewContainerRef, just for easy to understand -->
        <!-- <ng-container> assume -->
            
            <span>I am first span</span>

            ========================example1=======================
            <ng-container #vc>
                <span>I am the original Content</span>
                <!-- the line bellow will be inserted after running -->
                <!-- <div>I am inserted template</div> -->
            </ng-container>
            =======================================================
            

            ====================example2======================
            <!-- A Virtual ViewContainerRef will be wrapped around the span -->
            <!-- it will equal to  -->
            <!-- <ng-container> vc1 will refer to this node-->
                <span #vc1>I am another view Container</span>
                <!-- After the code running the next line will be inserted -->
                <!-- <div>I am inserted template</div> -->
            <!-- </ng-container> this is not exist, just from imaging -->
            ==================================================



            <ng-template #tmp1><div>I am inserted template</div></ng-template>


            <span>I am last span</span>


            ====================example3====================
             <!-- After the code running the next line will be inserted -->
                <!-- <div>I am inserted template</div> -->
            =================================================
        <!-- </ng-container> -->
    `
})
export class SampleComponent implements AfterViewInit {
    @ViewChild("vc", {read: ViewContainerRef}) example1: ViewContainerRef;
    @ViewChild("vc1, {read: ViewContainerRef}) example2: ViewContainerRef;
    @ViewChild('tmp1') tmp1:TemplateRef<any>;

    constructor(private componentLevelViewRef:ViewContainerRef){}

    ngAfterViewInit(): void {
        this.example1.createEmbeddedView(this.tmp1);
        this.example2.createEmbeddedView(this.tmp1);
        this.componentLevelViewRef.createEmbeddedView(this.tmp1);
    }
}
  • Injected in constructor of Component and Directive
    • In Directive, see the code bellow. What does the viewContainer bound to? the div.viewContainer is the viewContainer.
        //ts
        @Directive({
            selector:'[direct]',    
        })
        export class Directive{
            constructor(
                private viewContainer:ViewContainerRef,
            ){}
            ngAfterViewInit(): void {
                console.log(this.viewContainer.element.nativeElement);
            }
        }
        ============================================
        // html
        <!-- <ng-container> viewContener will refer to this -->
            <div [direct]>
                elements is attached by the directiive
            </div>
            <!-- embeded template will be appended here one by one -->
        <!-- </ng-container> -->
    

What can viewContainerRef do?

here we give a quick property list and method.

  • element: ElementRef; it refers to the contents under ng-container, we can access dom by calling viewContainerRef.element.nativeElement.
  • injector: Injector; get the injector of current viewContainerRef.
  • length: get the length of the views attached under this viewContainerRef. we can assume that viewContainerRef has this data [viewRef,viewRef,viewRef,...]
  • createEmbeddedView(templateRef:TemplateRef,context?:C,index?:number):EmbeddedViewRef This is an important function. see code bellow.
this.example1.createEmbeddedView(this.tmp1);
this.example2.createEmbeddedView(this.tmp1);
this.componentLevelViewRef.createEmbeddedView(this.tmp1);
  • createComponent(componentFactory:ComponentFactory,index?:number,injector?:Injector,projectableNodes?:any[][],ngModule?:NgModuleRef):ComponentRef

ElementRef

A wrapper around a native element. it is simple and straightforward.

class ElementRef<T = any> {
  constructor(nativeElement: T)
  nativeElement: T
}

we often use this in constructor by injecting. see code bellow, for both component and directive are the same behavior.

// typescript
@Directive({
    selector:'[my-select]',    
})
export class MyDirective{
    constructor(public el:ElementRef){}
    ngAfterViewInit(): void {
        // <div>Hello I am body</div>
        console.log(this.el.nativeElement);
    }

}

// html

<div my-select>Hello I am body</div>

TemplateRef

it represents an embedded template which can be used to instantiate embedded view. to be honest it is <ng-template>xxx</ng-template> in html.

how can we get refer to the TemplateRef

We ONLY have one way, through ViewChild, contentChild or injector, we can't new a templateRef instance in the fly.

let's see these examples

// html
<ng-template #sample1>I am Sample1</ng-template>
<ng-template pTemplate>I am Sample2</ng-template>



@Directive({
    selector: '[pTemplate]',
    host: {
    }
})
export class PrimeTemplate {
    
    @Input() type: string;
    
    @Input('pTemplate') name: string;
    
    constructor(public template: TemplateRef<any>) {}
    
    getType(): string {
        return this.name;
    }
}

// TS
@ViewChild('sample1',{read:TemplateRef}) sample1:TemplateRef<any>;
@ViewChild(PrimeTemplate) sample2:PrimeTemplate;
sample2.template   ------------it is the TemplateRef,
@ContentChild('sample3',{read:TemplateRef}) sample3:TemplateRef<any>,

What can TemplateRef do?

  • elementRef:ElementRef, it refer to parent view of this template.
    see the picture here
    This is example

  • createEmbeddedView(context:C) create an embedded view, difference with ViewContainerRef.createEmbeddedView()

// in templateRef
const viewRef = this.templateRef.createEmbeddedView({$implicit:'implicit param'});
this.viewContainerRef.insert(viewRef,this.this.viewContainerRef.length);

// in ViewContainerRef
this.viewContainerRef.createEmbeddedView(this.templateRef,{$implicit:'implicit param'});
posted @ 2021-05-30 15:42  kongshu  阅读(583)  评论(0编辑  收藏  举报