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> 表示组件名

示例

创建视图并插入

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-container 和 ng-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
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

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

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

    在Android开 发中,在Activity中关联视图View是一般使用setContentView方法,该方法一种参数是使用XML资源直接创 建:setContentView (int layout ...

  2. android 代码动态创建视图

    LinearLayout 如何动态设置 margin? LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayou ...

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

    MFC小程序截图: 一.在MFC中简单使用WebBrowser的ActiveX插件的方法: 见博文:  http://blog.csdn.net/supermanking/article/detail ...

  4. Angular动态创建组件之Portals

    这篇文章主要介绍使用Angular api 和 CDK Portals两种方式实现动态创建组件,另外还会讲一些跟它相关的知识点,如:Angular多级依赖注入.ViewContainerRef,Por ...

  5. angular4 动态创建组件 vs 动态创建模板

    实现  模拟场景:页面上"帮助"按钮的点击触发帮助文档的弹出框,且每个页面的帮助文档不一样 因此弹出框里的帮助文档是一个动态模板而不是动态组件 以下comp均代表Type类型的动态 ...

  6. 在WPF中使用依赖注入的方式创建视图

    在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...

  7. Oracle(创建视图)

    概念: 视图:所谓视图就是提取一张或者多张表的数据生成一个映射,管理视图可以同样达到操作原表的效果,方便数据的管理以及安全操作. 视图其实就是一条查询sql语句,用于显示一个或多个表或其他视图中的相关 ...

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

    动态管理视图: 具体的实例语句:  --关于语句执行的基本情况 select * from sys.dm_exec_query_stats --动态管理函数  需要提供参数  select top 1 ...

  9. oracle数据字典和动态性能视图

    数据字典和动态性能视图数据字典是oracle数据库中重要的组成部分,提高了数据库的一些系统信息.(静态信息)动态性能视图记载了例程启动后的信息.(动态信息) 数据字典记录了数据的系统信息,是只读表和动 ...

随机推荐

  1. 6.824 Lab 2: Raft 2C

    Part 2C Do a git pull to get the latest lab software. If a Raft-based server reboots it should resum ...

  2. :hover 鼠标同时触发两个元素变化

    HTML代码: <li> <span>4</span> <a href="#">巨型一号丝瓜水320ML</a>< ...

  3. 【FICO系列】SAP FICO 凭证错误:BKPFF$PRDCLN800在FI中达到的项目最大编号

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[FICO系列]SAP FICO 凭证错误:BK ...

  4. 卷积神经网络应用于MNIST数据集分类

    先贴代码 import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data mnist = inpu ...

  5. 【Qt开发】V4L2 API详解 Camera详细设置

    Camera的可设置项极多,V4L2 支持了不少.但Sam之前对这些设置的用法和涵义都是在看videodev2.h中边看边理解,感觉非常生涩.直到写这篇blog时,才发现v4l2有专门的SPEC来说明 ...

  6. 【Qt开发】【Linux开发】Qt程序在嵌入式设备(arm) 上运行,鼠标擦除界面的解决方案

    笔者最近想在arm开发板上,开发一个应用程序,经过网上查询发现qt作为跨平台开发软件很不错,于是便选择了qt开发,笔者的qt版本是4.8.6的.由于arm的主频太低,在arm上进行开发编译,效率会大大 ...

  7. kafka学习(八)

    管理kafka     主题操作 1.在集群里创建一个主题需要用到3个参数.这些参数是必须提供的,尽管有些已经有broker级别的默认值.   主题名字,想要创建的主题的名字,主题名字可以包含字母,数 ...

  8. 2019CSP-S游记(?)

    认识我的人都知道,我懒得写算法和模拟赛的博客,但是游记就不一样了,它比较好玩. Day0 中午随便收拾了下就坐高铁出发了,一个小时左右就到南昌了,随后坐公交,再步行到宾馆安置(也没多远). 宾馆离学校 ...

  9. Two modules in a project cannot share the same content root报错解决方案

    观察上方是否出现两个同样的项目,删除不需要的那个,我觉得是因为两个项目同时引用一个根目录文件导致的.

  10. P3951小凯的疑惑

    这是2017年提高组的第一题,是一个小学奥数题?听说很多大佬爆零了,我AC了,,, 这个题首先给出两个素数,问取任意个这两个素数之和不可以达到的最大的数是多少?拿到这个题首先很蒙,于是试了试样例,并没 ...