錨元素,指定該容器在包含視圖中的位置。在渲染好的視圖中會變成錨點元素的兄弟。能夠在元素上放置注入了 ViewContainerRef
的 Directive
來訪問元素的 ViewContainerRef
api
是否是聽着有點矇蔽,沒事讓我慢慢幫你理解本質app
angular-master\angular-master\packages\core\test\acceptance\view_insertion_spec.ts
dom
插入的參數this
createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C> { const viewRef = templateRef.createEmbeddedView(context || <any>{}); this.insert(viewRef, index); return viewRef; } index未指定插入最後,咱們能夠知道,索引默認從0開始,填寫第幾個就是在哪一個位置插入
<ng-template #simple> <app-a></app-a> </ng-template> <div #container></div> export class TwoComponent implements OnInit, AfterViewInit{ @ViewChild('container', {read: ViewContainerRef, static: true}) container: ViewContainerRef = null!; @ViewChild('simple', {read: TemplateRef, static: true}) simple: TemplateRef<any> = null!; constructor(private changeDetector: ChangeDetectorRef) {} ngAfterViewInit() { //直接插入 this.container.createEmbeddedView(this.simple) // 指定特定的插入位置 this.container.createEmbeddedView(this.simple, {}, 2) // 若是插入的是組件須要運行變動檢測,若是插入的是dom,就不須要,主要看是否有值傳入裏面 this.changeDetector.detectChanges(); } }
子代的插入問題spa
<app-a> <div>xxx</div> <app-b></app-b> </app-a> app-a <ng-template #projection> <ng-content></ng-content> </ng-template> <div #container> <h1>dddd</h1> </div> export class AComponent implements OnInit, AfterViewInit, AfterContentInit, OnDestroy, AfterContentChecked { @ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef = null!; @ViewChild('projection',{read:TemplateRef}) projection: TemplateRef<any> = null!; ngAfterViewInit() { // 默認插入的位置 this.container.createEmbeddedView(this.projection); // 修改位置,咱們發現dom在下面了,組件在上面,由於ng-content只能有一個,因此從新插入會替換掉以前的 this.container.createEmbeddedView(this.projection,{},0); } }
插入的時候特殊的問題點,報錯的解決方案code
<ng-template #subContainer> <div class="dynamic" *ngIf="true">test</div> </ng-template> <div #container></div> export class TwoComponent implements OnInit, AfterViewInit, AfterViewChecked, AfterContentInit, AfterContentChecked { constructor(private changeDetector: ChangeDetectorRef) { } @ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef = null!; @ViewChild('subContainer', {read: TemplateRef}) subContainer: TemplateRef<any> = null!; ngAfterViewInit() { this.view1 = this.container.createEmbeddedView(this.subContainer); this.view1 = this.container.createEmbeddedView(this.subContainer, null, 0); //若是不寫下面的能夠會報錯,在這裏運行變動檢測來避免 由於值被傳遞給ngIf this.changeDetector.detectChanges(); } }
<ng-template #insert> <div>insertA</div> </ng-template> <ng-template #before> <div>insertB</div> </ng-template> <div> // 介紹下爲何要 #vi="vi" // #vi 是描點定義的變量 'vi' 是別名的使用,就相似 #a="vi" // vi的別名複製給 #a // 頁面頁面上面的其餘元素經過 vi.方法 使用指令裏面的方法 // 第二點,在ts中咱們使用@ViewChild 也須要#vi 指定的變量拿到其中的位置 <ng-template #vi="vi" viewInserter> </ng-template> </div> <button (click)="insertA()">Click</button> ---------- @ViewChild('before', {static: true}) beforeTpl!: TemplateRef<{}>; @ViewChild('insert', {static: true}) insertTpl!: TemplateRef<{}>; @ViewChild('vi', {static: true}) viewInsertingDir!: ViewInsertingDir; insertA() { const beforeView = this.beforeTpl.createEmbeddedView({}); // 變動檢測「before視圖」來建立全部子視圖 beforeView.detectChanges(); this.viewInsertingDir.insert(beforeView, this.insertTpl); } ----------- 指令 @Directive({ selector: '[viewInserter]', exportAs:'vi' }) export class ViewInserterDirective { constructor(private _vcRef:ViewContainerRef) { } insert(beforeView: ViewRef, insertTpl: TemplateRef<{}>) { this._vcRef.insert(beforeView, 0); this._vcRef.createEmbeddedView(insertTpl, {}, 0); } }
指令component
@Directive({ selector: '[viewInserter]', exportAs:'vi' }) export class ViewInserterDirective { constructor(private _vcRef:ViewContainerRef) { } insert(beforeView: ViewRef, insertTpl: TemplateRef<{}>) { this._vcRef.insert(beforeView, 0); this._vcRef.createEmbeddedView(insertTpl, {}, 0); } }
<ng-template #insert>insert</ng-template> <div><ng-template #vi="vi" viewInserter></ng-template></div> <button (click)="insertA()">Click</button> export class TwoComponent implements OnInit{ constructor(private _cfr: ComponentFactoryResolver, private _injector: Injector) {} @ViewChild('insert', {static: true}) insertTpl!: TemplateRef<{}>; @ViewChild('vi', {static: true}) viewInserterDirective!: ViewInserterDirective; insertA() { // 建立一個動態組件視圖,做爲「insert before」視圖 const componentFactory = this._cfr.resolveComponentFactory(AComponent); // 添加服務 const beforeView = componentFactory.create(this._injector).hostView; // 變動檢測「before視圖」來建立全部子視圖 beforeView.detectChanges(); this.viewInserterDirective.insert(beforeView, this.insertTpl); } }
忽然疑惑一點爲啥我加入一個新的變量的時候,就會報錯找不到vi
blog
@ViewChild('vi', {static: true}) 其實咱們把{static:true} 去掉或者改爲false就能夠啦
<ng-template #tpl1> <div #foo>Foo 1</div> </ng-template> <div #foo>Betwean tpl _definitions_</div> <ng-template #tpl2 let-idx="idx"> <div #foo>Foo 2</div> </ng-template> <ng-template viewInserter #vi="vi"></ng-template> <hr> <button (click)="vi.insert(tpl1)">Insert Foo1</button> <button (click)="vi.insert(tpl2)">Insert Foo2</button> <button (click)="vi.clear()">clear</button> <button (click)="vi.remove()">刪除第4個</button> <!--另外一種使用方式--> <button (click)="clickMode()">click</button> @ViewChild(ViewInserterDirective) vc: ViewInserterDirective; // 這也是一種方式 clickMode(){ this.vc.insert(this.tpl2) }
指令中索引
@Directive({ selector: '[viewInserter]', exportAs:'vi' }) export class ViewInserterDirective { constructor(private _vcRef: ViewContainerRef,private changeDetector: ChangeDetectorRef) {} insert(tpl: TemplateRef) { this._vcRef.createEmbeddedView(tpl); } clear() { this._vcRef.clear(); } //默認出現報錯記得檢測更新 remove(index?: number) { this._vcRef.remove(index); } //能夠還不懂 move(viewRef: ViewRef, index: number) { this._vcRef.move(viewRef, index); } }
試不試感受模模糊糊的,那咱們從新編寫讓你們看的更清晰些three
<ng-template #one> <div>one</div> </ng-template> <ng-template #two> <div>two</div> </ng-template> <ng-template #three> <div>three</div> </ng-template> <h1>--------</h1> <div viewInserter></div> <button (click)="increase()">增長one</button> <button (click)="increaseT(two)">增長two另外一種模式</button> <button (click)="increaseT(three)">增長two另外一種模式</button> <button (click)="remove()">刪除1</button> <button (click)="clickMove(0,3)">移動0,3</button> <button (click)="clear()">刪除所有</button>
export class TwoComponent implements OnInit, AfterViewInit { constructor(private _cfr: ComponentFactoryResolver, private _injector: Injector) { } @ViewChild('one') one!: TemplateRef<any>; @ViewChild('two') two!: TemplateRef<any>; @ViewChild('three') three!: TemplateRef<any>; @ViewChild(ViewInserterDirective) vd: ViewContainerRef; // 移動 clickMove(start, end) { // 查詢 this.vd.move(start,end) } ngOnInit(): void { } //增 increase() { this.vd.insert(this.one) } //增 increaseT(tpl: Comp) { this.vd.insert(tpl) } //刪除 remove(){ this.vd.remove(1) } //刪除所有 clear(){ this.vd.clear() } }
指令
@Directive({ selector: '[viewInserter]', }) export class ViewInserterDirective { constructor(private _vcRef: ViewContainerRef, private changeDetector: ChangeDetectorRef) { } insert(tpl: TemplateRef<unknown>) { this._vcRef.createEmbeddedView(tpl); } clear() { this._vcRef.clear(); } //默認出現報錯記得檢測更新 remove(index?: number) { this._vcRef.remove(index); } move(start, end) { this._vcRef.move(this._vcRef.get(start), end); } }
忽然想一想那默認探究下ViewContainerRef
具體有哪些api呢
explore(){ // 查找找不到範圍-1 // this._vcRef.indexOf(this._vcRef.get(0)); // 拿到當前指令的dom // console.log(this._vcRef.element.nativeElement); // 插入 // insert(viewRef: ViewRef, index?: number): ViewRef //從這個容器中分離視圖而不銷燬它。 // *與' insert() '一塊兒使用來移動當前容器中的視圖。 // * @param index要分離的視圖基於0的索引。 // *若是不指定,容器中的最後一個視圖將被分離。 this._vcRef.detach(3) }
@NgModule({ declarations: [ AComponent], entryComponents:[AComponent],// 動態組件須要在模塊中引入
<ng-container #container></ng-container> <button (click)="createComp()">++</button> export class TwoComponent implements OnInit, AfterViewInit { constructor(private _cfr: ComponentFactoryResolver) { } @ViewChild('container', {read: ViewContainerRef}) vcRef!: ViewContainerRef; createComp() { const factory = this._cfr.resolveComponentFactory(AComponent); this.vcRef.createComponent(factory) } }
搞混
ElementRef
DOM
templateRef
就是ng-template
上的
<ng-template #foo></ng-template> @ViewChild('foo', {static: true}) foo!: TemplateRef<any>; <span #foo></span> @ViewChild('foo', {static: true}) foo!: ElementRef;