ViewContainerRef 动态创建视图

Angular DOM 操作

相关的APIs和类:

  • 查询DOM节点

    • template variable ref: 模版变量引用,相当于react中的ref
    • ViewChild: 查询DOM,返回单个元素引用
    • ViewChildren: 返回一个QueryList对象,包含一系列元素
  • ElementRef: 元素引用
    • 查询的方式获取,比如 @ViewChild(‘myInput‘) inputElm: ElementRef
    • 依赖注入的方式,获取宿主元素,比如 constructor(private elem: ElementRef){}
  • TemplateRef: 模板引用
    • 查询的方式, 比如 <ng-template #tpl></ng-template>
  • ViewContainerRef: 视图容器,包含创建angular视图的方法和操作视图的apis
  • ViewRef: 视图引用,angular最小的UI单元,创建的视图的返回类型就是ViewRef
  • angular中的2种类型的视图
    • 插入式视图Embedded Views
    • 宿主视图 component instance views,即组件实例视图
  • EmbeddedViewRef: 插入式视图引用,上面创建插入式视图防护的类型
  • ComponentRef<C>: 组件视图引用,创建hostView时返回的类型 <C> 表示组件名

https://upload-images.jianshu.io/upload_images/1203223-b115a5ab14bdbb05.png

Angular+DOM操作.png

示例

创建视图并插入

import {
  Component,
  ViewChild,
  TemplateRef,
  ViewContainerRef,
  ViewRef,
  AfterViewInit
} from ‘@angular/core‘;

@Component({
  selector: ‘app-sample‘,
  template: `
    <span>我是第一个span标签</span>
    <ng-container #vc></ng-container>
    <span>我是第二个span标签</span>
    <ng-template #tpl>
      <div>我是模版里面的div标签</div>
    </ng-template>
  `
})
export class SampleComponent implements AfterViewInit {
  // 查询元素, {read: ViewContainerRef} 不能省略,因为angular无法推断出它是一个容器
  @ViewChild(‘vc‘, {read: ViewContainerRef}) vc: ViewContainerRef;
  // {read: TemplateRef} 可以省略
  @ViewChild(‘tpl‘) tpl: TemplateRef<any>;

  constructor() {}

  ngAfterViewInit() {
    // 创建一个插入式视图, 一般插入式视图都对应的是模版视图
    const tplView: ViewRef = this.tpl.createEmbeddedView(null);

    // 插入到容器当中 使用视图容器操作视图的方法insert
    this.vc.insert(tplView);

  }
}

最后得到的结果是

<app-sample>
    <span>我是第一个span标签</span>
    <!--template bindings={}-->
    <div>我是模版里面的div标签</div>
    <span>我是第二个span标签</span>
    <!--template bindings={}-->
</app-sample>

可以看出 ng-containerng-template 最后编译后都变为了注释

<!--template bindings={}-->

使用 ngTemplateOutlet 指令

angular为创建插入式视图提供了 ngTemplateOutlet 指令,这个和 router-outlet 功能类似,为模版提供了一个进入的入口,上面的例子可以改写为

import {
  Component,
} from ‘@angular/core‘;

@Component({
  selector: ‘app-sample‘,
  template: `
    <span>我是第一个span标签</span>
    <ng-container [ngTemplateOutlet]="tpl"></ng-container>
    <span>我是第二个span标签</span>
    <ng-template #tpl>
      <div>我是模版里面的div标签</div>
    </ng-template>
  `
})
export class SampleComponent {
}

可以看出这种十分的方便,在饿了吗angular tooltip组件中就使用到了这个指令


@Component({
  selector: ‘el-tooltip‘,
  template: `
    <div style="position: relative; display: inline-block;">
      <div [class]="‘el-tooltip__popper is-‘ + effect + ‘ ‘ + popperClass"
        style="left: -20000px; top: 0; position: absolute;"
        [@fadeAnimation]="!showPopper" [attr.x-placement]="xPlacement" #popperContent>
        <div x-arrow class="popper__arrow" [hidden]="!visibleArrow"></div>
        <ng-template [ngTemplateOutlet]="tip"></ng-template>  # 此处使用到了ngTemplateOutlet指令
      </div>
      <ng-content></ng-content>
    </div>
  `,
  animations: [fadeAnimation],
})
export class ElTooltip implements AfterContentInit {
  @ContentChild(‘tip‘) tip: TemplateRef<any>
}

使用时:

<el-tooltip>
  <ng-template #tip>我是将要插入的模版内容<ng-template>
</el-tooltip>

ngComponentOutlet

这个指令和上面的 ngTemplateOutlet类似,但是它将创建一个 host view(组件的实例),而不是插入式视图,使用方式

<ng-container *ngComponentOutlet="ColorComponent"></ng-container>

ViewContainerRef源码

import { Injector } from ‘../di/injector‘;
import { ComponentFactory, ComponentRef } from ‘./component_factory‘;
import { ElementRef } from ‘./element_ref‘;
import { NgModuleRef } from ‘./ng_module_factory‘;
import { TemplateRef } from ‘./template_ref‘;
import { EmbeddedViewRef, ViewRef } from ‘./view_ref‘;
/**
 * Represents a container where one or more Views can be attached.
 * 表示能够被一个或者多个视图附着的容器
 * The container can contain two kinds of Views. Host Views, created by instantiating a
 * {@link Component} via {@link #createComponent}, and Embedded Views, created by instantiating an
 * {@link TemplateRef Embedded Template} via {@link #createEmbeddedView}.
 * 容器能够包含2种类型的视图: 宿主视图(组件实例) 和 插入式视图(使用模版创建的视图)
 * The location of the View Container within the containing View is specified by the Anchor
 * `element`. Each View Container can have only one Anchor Element and each Anchor Element can only
 * have a single View Container.
 *
 * Root elements of Views attached to this container become siblings of the Anchor Element in
 * the Rendered View.
 * 插入的视图的根元素会成为视图容器的兄弟节点,而不是之间插入到容器中,这点和router-outlet插入组件的方式一致
 *
 * To access a `ViewContainerRef` of an Element, you can either place a {@link Directive} injected
 * with `ViewContainerRef` on the Element, or you obtain it via a {@link ViewChild} query.
 * 可以通过注入的方式范围viewContainerRef 和 通过 ViewChild 查询的方式获取viewContainerRef
 * @stable
 */
export declare abstract class ViewContainerRef {
    /**
     * Anchor element that specifies the location of this container in the containing View.
     * <!-- TODO: rename to anchorElement -->
     */
    readonly abstract element: ElementRef;
    readonly abstract injector: Injector;         // 注入器, 用于动态创建组件中
    readonly abstract parentInjector: Injector;   // 父注入器, 如果组件自身没有提供注入器,使用父注入器
    /**
     * Destroys all Views in this container. 销毁容器内的所有视图
     */
    abstract clear(): void;
    /**
     * Returns the {@link ViewRef} for the View located in this container at the specified index.
     * 返回视图引用索引
     */
    abstract get(index: number): ViewRef | null;

    /**
     *  获取视图容器的数量
     * Returns the number of Views currently attached to this container.
     */
    readonly abstract length: number;

    /**
     * 实例化一个插入式视图,可以插入到指定的索引位置,如果不指定索引,将放到最后面
     * Instantiates an Embedded View based on the {@link TemplateRef `templateRef`} and inserts it
     * into this container at the specified `index`.
     *
     * If `index` is not specified, the new View will be inserted as the last View in the container.
     *
     * Returns the {@link ViewRef} for the newly created View. 返回一个视图引用
     */
    abstract createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): EmbeddedViewRef<C>;

    /**
     * 实例化单个组件,插入到宿主视图中,可以插入到指定的索引位置,如果不指定索引,将放到最后面
     * Instantiates a single {@link Component} and inserts its Host View into this container at the
     * specified `index`.
     *
     * The component is instantiated using its {@link ComponentFactory} which can be
     * obtained via {@link ComponentFactoryResolver#resolveComponentFactory}.
     * 组件通过 ComponentFactory 实例化, 而组件工厂可以通过 ComponentFactoryResolver来创建
     * If `index` is not specified, the new View will be inserted as the last View in the container.
     *
     * You can optionally specify the {@link Injector} that will be used as parent for the Component.
     *
     * Returns the {@link ComponentRef} of the Host View created for the newly instantiated Component.
     */
    abstract createComponent<C>(componentFactory: ComponentFactory<C>, index?: number, injector?: Injector, projectableNodes?: any[][], ngModule?: NgModuleRef<any>): ComponentRef<C>;

    /**
     * 插入视图
     * Inserts a View identified by a {@link ViewRef} into the container at the specified `index`.
     *
     * If `index` is not specified, the new View will be inserted as the last View in the container.
     *
     * Returns the inserted {@link ViewRef}.
     */
    abstract insert(viewRef: ViewRef, index?: number): ViewRef;

    /**
     * 依据索引移动视图
     * Moves a View identified by a {@link ViewRef} into the container at the specified `index`.
     *
     * Returns the inserted {@link ViewRef}.
     */
    abstract move(viewRef: ViewRef, currentIndex: number): ViewRef;

    /**
     * 返回视图的索引位置
     * Returns the index of the View, specified via {@link ViewRef}, within the current container or
     * `-1` if this container doesn‘t contain the View.
     */
    abstract indexOf(viewRef: ViewRef): number;

    /**
     * 移除视图
     * Destroys a View attached to this container at the specified `index`.
     *
     * If `index` is not specified, the last View in the container will be removed.
     */
    abstract remove(index?: number): void;

    /**
     * 将视图从当前容器中分离
     * Use along with {@link #insert} to move a View within the current container.
     *
     * If the `index` param is omitted, the last {@link ViewRef} is detached.
     */
    abstract detach(index?: number): ViewRef | null;
}

2种视图

模版视图

也称之为插入式视图

<ng-template #tpl></ng-template>
<ng-container #vc><ng-container>

class SampleComponent implments AfterViewInit {
  @ViewChild(‘tpl‘) tpl: Template<any>;
  @ViewChild(‘vc‘, {read: ViewContainerRef}) vc: ViewContainerRef;
  ngAfterViewInit() {
    let embeddedView: ViewRef = this.tpl.createEmbeddedView(null);
    this.vc.insert(embeddedView);
  }
}

宿主视图

和组件相关

<ng-container #vc><ng-container>

class SampleComponent implments AfterViewInit {
  @ViewChild(‘tpl‘) tpl: Template<any>;
  @ViewChild(‘vc‘, {read: ViewContainerRef}) vc: ViewContainerRef;
  componentRef: ComponentRef<ColorComponent>;

  constructor(private injector: Injector, provate cfr: ComponentFactoryResolver) {}
  ngAfterViewInit() {
    const factory = this.cfr.rosolveComponentFactory(ColorComponent); // 创建组件工厂
    this.componentRef = this.vc.createComponent(factory); // 创建组件引用
    // this.componentRef = factory.create(this.injector); // 创建注入器
    // let view: ViewRef = componentRef.hostView;     // 创建宿主视图
    // this.vc.insert(view);
  }
}

最后动态创建的组件需要添加到 entryComponent

示例:

文章参考:

作者:JamesSawyer
链接:https://www.jianshu.com/p/40dec12a278c
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

原文地址:https://www.cnblogs.com/rjjs/p/11275960.html

时间: 2024-11-09 16:18:51

ViewContainerRef 动态创建视图的相关文章

Android编程动态创建视图View的方法

在Android开 发中,在Activity中关联视图View是一般使用setContentView方法,该方法一种参数是使用XML资源直接创 建:setContentView (int layoutResID),指定layout中的一个XML的ID即可,这种方法简单.另一个方法是 setContentView(android.view.View),参数是指定一个视图View对象,这种方法可以使用自定义的视图类. 在一些场合中,需要对View进行一些定制处理,比如获取到Canvas进行图像绘制,

SQL 存储过程动态创建视图

CREATE PROCEDURE  CREAR_PF_VIEW  AS BEGIN DECLARE @sql VARCHAR(MAX) SET @sql = 'CREATE VIEW dbo.V_PF AS select * from (select distinct XMBH as XMBH  From YDS_SHPB ) X1 ' DECLARE @uid VARCHAR(MAX) DECLARE @sql_ls VARCHAR(MAX) DECLARE cur CURSOR      

MFC小程序003------MFC使用WebBrowser组件,在对话框中创建滚动视图,动态创建一个静态文本控件并设置鼠标单击的消息响应

MFC小程序截图: 一.在MFC中简单使用WebBrowser的ActiveX插件的方法: 见博文:  http://blog.csdn.net/supermanking/article/details/5866213 二.在对话框中创建视图(我这里创建的是一个滚动视图): 见博文: http://blog.csdn.net/clever101/article/details/4873994 http://blog.csdn.net/clever101/article/details/37790

Angular动态创建组件之Portals

这篇文章主要介绍使用Angular api 和 CDK Portals两种方式实现动态创建组件,另外还会讲一些跟它相关的知识点,如:Angular多级依赖注入.ViewContainerRef,Portals可以翻译为 门户 ,我觉得放到这里叫 入口 更好,可以理解为动态创建组件的入口,类似于小程序或者Vue中的Slot. cdk全名Component Development Kit 组件开发包,是Angular官方在开发基于Material Design的组件库时抽象出来单独的一个开发包,里面

Oracle(创建视图)

概念: 视图:所谓视图就是提取一张或者多张表的数据生成一个映射,管理视图可以同样达到操作原表的效果,方便数据的管理以及安全操作. 视图其实就是一条查询sql语句,用于显示一个或多个表或其他视图中的相关数据.视图将一个查询的结果作为一个表来使用,因此视图可以被看作是存储查询结果的一个虚拟表.视图来源于表,所有对视图数据的修改最终都会被反映到视图的基表中,这些修改必须服从基表的完整性约束. 视图的存储 与表不同,视图不会要求分配存储空间,视图中也不会包含实际的数据.视图只是定义了一个查询,视图中的数

Oracle的基本操作-数据字典和动态性能视图的介绍

三.数据字典和动态性能视图的介绍 1. 数据库的数据字典 (1) DBA_   全库所有的某种对象的数据字典       只有系统用户才有权限查询 (2) ALL_   当前用户创建的对象 + 其他用户授予当前用户使用的对象       当前用户 (3) USER_  当前用户创建的对象       当前用户 操作实例:解锁一个用户hr并改密码SQL> alter user hr account unlock; User altered. SQL> alter user hr identifi

SqlServer性能优化 手工性能收集动态管理视图(三)

动态管理视图: 具体的实例语句:  --关于语句执行的基本情况 select * from sys.dm_exec_query_stats  --动态管理函数  需要提供参数  select top 10 execution_count,total_worker_time,t.text,total_worker_time/execution_count      as avgcpu from  sys.dm_exec_query_stats as s  cross apply sys.dm_ex

07---关于动态创建和销毁带动画的UIVew

在我们做开发的过程中经常会遇到在你触发了某个动作之后,需要动态添加一个带动画的UIView,执行完以后就销毁这个UIView #pragma mark 展示最新微博的数目 - (void)showNewStatusCount:(int)count { // 1.创建按钮 UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom]; btn.enabled = NO; // 设置按钮禁用 btn.adjustsImageWhenDisa

FineUIMvc随笔 - 动态创建表格列

声明:FineUIMvc(基础版)是免费软件,本系列文章适用于基础版. 用户需求 用户希望实现动态创建表格列,在 WebForms 中,我们通过在 Page_Init 中创建列来实现: 但是在 MVC 中,如果还想着 WebForms 的那一套,想着怎么才能在 Controller 中访问 View 中的表格控件,这是行不通的. 我曾写过一个系列文章<ASP.NET MVC快速入门(MVC5+EF6)>,开篇就讲到了 MVC 中的页面的生成流程: 这个页面之所以能够呈现在我们眼前,经历了三个主