Angular之ViewContainerRef、ViewRef、EmbeddedViewRef、TemplateRef

本文详细介绍了Angular中的关键视图概念,包括ViewContainerRef用于动态创建和管理视图,EmbeddedViewRef表示嵌入式视图,ViewRef作为通用视图接口,以及TemplateRef用于模板引用。文章还探讨了视图树的概念,并通过ngTemplateOutlet指令的实际应用进行了说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. ViewContainerRef 视图容器

ViewContainerRef 可以将一个或多个视图附着到组件中的容器。通过它可以动态创建内嵌视图、动态创建组件,可以添加、移动、删除视图容器中的视图。

abstract class ViewContainerRef {
  abstract element: ElementRef
  abstract injector: Injector
  abstract parentInjector: Injector
  abstract length: number
  abstract clear(): void
  abstract get(index: number): ViewRef | null

  // 创建内嵌视图(实例化一个内嵌视图,并把它插入到该容器中),参数1:模板引用TemplateRef,参数2:要挂在在内嵌视图上的上下文对象。参数3:从 0 开始的索引,表示新视图要插入到当前容器的哪个位置。 如果没有指定,就把新的视图追加到最后。最终返回值是个内嵌视图EmbeddedViewRef。
  abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C>  ⭐️⭐️

  // 在视图容器上动态创建组件
  abstract createComponent<C>(componentType: Type<C>, options?: { index?: number; injector?: Injector; ngModuleRef?: NgModuleRef<unknown>; projectableNodes?: Node[][]; }): ComponentRef<C>  ⭐️⭐️

  // 往视图容器中插入视图
  abstract insert(viewRef: ViewRef, index?: number): ViewRef    ⭐️⭐️

  // 移动视图容器中的某个视图
  abstract move(viewRef: ViewRef, currentIndex: number): ViewRef  ⭐️⭐️

  // 根据给定的视图检索它在视图容器中的index
  abstract indexOf(viewRef: ViewRef): number    ⭐️⭐️

  // 将要移除的视图在视图容器中的对应的index传给remove方法来进行视图删除
  abstract remove(index?: number): void    ⭐️⭐️

  abstract detach(index?: number): ViewRef | null
}

2. EmbeddedViewRef 嵌入式视图

  • 表示视图容器中的 Angular 视图。嵌入视图可以从在模板中定义它的宿主组件之外的组件中引用,也可以由 TemplateRef 进行独立定义。
  • 通过在视图容器ViewContainerRef插入移动删除 嵌套视图EmbeddedViewRef来更改元素的结构。
abstract class EmbeddedViewRef<C> extends ViewRef {
  // 该视图的上下文,继承自锚点元素。
  abstract context: C      ⭐️⭐️
  // 该内嵌视图的根节点
  abstract rootNodes: any[]

  // 继承自 core/ViewRef
  abstract destroyed: boolean
  abstract destroy(): void
  abstract onDestroy(callback: Function): any

  // 继承自 core/ChangeDetectorRef
  abstract markForCheck(): void
  abstract detach(): void
  abstract detectChanges(): void
  abstract checkNoChanges(): void
  abstract reattach(): void
}

3. ViewRef 视图

表示一个 Angular 视图。其子类:EmbeddedViewRef。

(1)视图 view

  • 视图是可显示元素的最小分组单位,它们会被同时创建和销毁。 Angular 在一个或多个指令 (directive) 的控制下渲染视图

  • 组件 (component) 类及其关联的模板 (template)定义了一个视图。 具体实现上,视图由一个与该组件相关的 ViewRef 实例表示。 直属于某个组件的视图叫做宿主视图。 通常会把视图组织成一些视图树(view hierarchies)。

  • 视图中各个元素的属性可以动态修改以响应用户的操作,而这些元素的结构(数量或顺序)则不能。我们可以通过在它们的视图容器中插入、移动或移除内嵌视图来修改这些元素的结构。

  • 当用户在应用中导航时(比如使用路由器),视图树可以动态加载或卸载。

(2)视图树

  • 一棵相关视图的树,它们可以作为一个整体行动。其根视图就是组件的宿主视图。

  • 宿主视图可以是内嵌视图树的根,它被收集到了宿主组件上的一个视图容器(ViewContainerRef)中。视图树是 Angular 变更检测的关键部件之一。

  • 视图树和组件树并不是一一对应的。那些嵌入到指定视图树上下文中的视图也可能是其它组件的宿主视图。那些组件可能和宿主组件位于同一个 NgModule 中,也可能属于其它 NgModule。

4. TemplateRef 模板引用

它表示一个内嵌模板,它可用于实例化内嵌的视图。 要想根据模板实例化内嵌的视图,请使用 ViewContainerRef 的 createEmbeddedView() 方法。

abstract class TemplateRef<C> {
  abstract elementRef: ElementRef
  abstract createEmbeddedView(context: C): EmbeddedViewRef<C>
}

5. 实际应用 – ngTemplateOutlet指令的实现

import {Directive, EmbeddedViewRef, Input, OnChanges,  SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';

@Directive({selector: '[ngTemplateOutlet]'})
export class NgTemplateOutlet implements OnChanges {
  private _viewRef: EmbeddedViewRef<any> | null = null;

  // 附加到EmbeddedViewRef的上下文对象。 这应该是一个对象,对象的键将可用于本地模板 `let` 的绑定声明。在上下文对象中使用键 $implicit 会将其值设置为默认值。
  @Input() public ngTemplateOutletContext: Object | null = null;

  // 定义模板引用和可选的模板上下文对象的字符串。
  @Input() public ngTemplateOutlet: TemplateRef<any> | null = null;

  constructor(
    // 注入视图容器
    private _viewContainerRef: ViewContainerRef
  ) {}

  /**
   * OnChanges 是一个生命周期钩子,当指令的任何一个可绑定属性(ngTemplateOutlet或ngTemplateOutletContext)发生变化时调用。
   * 定义一个 ngOnChanges() 方法来处理这些变更。
   * @param changes
   */
  ngOnChanges(changes: SimpleChanges) {
    if (changes['ngTemplateOutlet']) {
      // 1 如果传入了一个新的目标引用,则移除当前的视图。
      const viewContainerRef = this._viewContainerRef;

      if (this._viewRef) {
        // 1.1 如果当前有视图,找到该视图在当前视图容器中的索引,并根据索引移除当前视图
        viewContainerRef.remove(viewContainerRef.indexOf(this._viewRef));
      }

      // 1.2 在当前视图容器中添加新的内嵌视图,参数1是模板引用TemplateRef,参数2是要挂载在该内嵌视图的上下文对象。
      this._viewRef = this.ngTemplateOutlet
        ? viewContainerRef.createEmbeddedView(this.ngTemplateOutlet, this.ngTemplateOutletContext)
        : null;
    } else if (
      // 如果传入了一个新的上下文对象 且 当前有视图,则更新当前视图的上下文。
      this._viewRef &&
      changes['ngTemplateOutletContext'] &&
      this.ngTemplateOutletContext
    ) {
      this._viewRef.context = this.ngTemplateOutletContext;
    }
  }
}

End,看Angular的源码实现是一件很开心很幸福的事情😄

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值