使用ViewContainerRef探索Angular DOM操作技术

每当我阅读中遇到,关于Angular中使用DOM的内容时,总会看到一个或几个这样的类:ElementRef,TemplateRef,ViewContainerRef等等。 不幸的是,虽然其中的一些被Angular文档或相关文章所讲述,但是我还没有找到完整的描述以及这些它们是如何工作的。

如果你来自angular.js世界,那么你知道操纵DOM是相当容易的。Angular注入DOM elementRef到构造函数中,你可以查询组件模板中的任何节点,添加或删除子节点,修改样式等。但是,这种方法有一个主要的缺点 - 它紧紧地绑定到浏览器平台。

新的Angular版本运行在不同的平台上 - 浏览器,移动平台等。 因此,站在平台特定的API和框架接口之间需要抽象层次。Angular中,这些抽象成为以下引用类型的形式:ElementRef,TemplateRef,ViewRef,ComponentRef和ViewContainerRef。 在本文中,我们将详细介绍每种引用类型,并展示如何使用它们来操作DOM。

@ViewChild

在我们探索DOM抽象之前,让我们了解如何在组件/指令类中访问这些抽象。 Angular提供了一种称为DOM查询的机制。 它以@ViewChild和@ViewChildren装饰器的形式出现。 它们的行为相同,只有前者返回一个引用,后者则返回多个引用作为QueryList对象。 在这篇文章的例子中,我将主要使用ViewChild装饰器。

通常,这些装饰器与模板引用变量配对使用。 模板引用变量只是对模板中的DOM元素的命名引用。 您可以将其视为与html元素的id属性类似的东西。 用模板引用标记DOM元素,然后使用ViewChild装饰器在类中查询它。 这里是基本的例子:

@Component({selector: 'sample',template: `<span #tref>I am span</span>`
})
export class SampleComponent implements AfterViewInit {@ViewChild("tref", {read: ElementRef}) tref: ElementRef;ngAfterViewInit(): void {// outputs `I am span`console.log(this.tref.nativeElement.textContent);}
}

ViewChild装饰器的基本语法如下:

@ViewChild([reference from template], {read: [reference type]});

在这个例子中,你可以看到我在html中指定了tref作为模板引用名,并且接收到与这个元素相关的ElementRef。 读取的第二个参数并不总是必需的,因为Angular可以通过DOM元素的类型来推断引用类型。 例如,如果它是一个简单的HTML元素(如span),那么angular将返回ElementRef。 如果它是一个模板元素,它将返回TemplateRef。不过一些引用,如ViewContainerRef不能被推断,并且必须在读参数中特别要求。 其他的,像ViewRef不能从DOM返回,必须手动构造。

ElementRef

这是最基本的抽象。 如果你观察它的类结构,你会发现它只保存了它所关联的本地元素。 对于访问本地DOM元素非常有用,我们可以在这里看到:

// outputs `I am span`
console.log(this.tref.nativeElement.textContent);

不过,Angular团队不鼓励这种用法。 这不仅会带来安全风险,还会在应用程序和渲染层之间造成紧密耦合,这使得在多个平台上运行应用程序变得困难。 我相信这不是对nativeElement的访问,而是打破了抽象,而是像textContent一样使用特定的DOM API。 但是后面你会看到,在Angular中实现的DOM操作心智模型几乎不需要这样一个较低级别的访问。

可以使用ViewChild装饰器为任何DOM元素返回ElementRef。 但是,由于所有组件都驻留在自定义DOM元素中,并且所有指令都应用于DOM元素,因此组件和指令类可以通过DI机制获取与其主机元素关联的ElementRef实例:

@Component({selector: 'sample',...
export class SampleComponent{constructor(private hostElement: ElementRef) {//outputs <sample>...</sample>console.log(this.hostElement.nativeElement.outerHTML);}

因此,虽然组件可以通过DI访问其主机元素,但ViewChild装饰器通常用于在其视图(模板)中获取对DOM元素的引用。 反之亦然,指令没有视图,他们通常直接与他们所附的元素。

模板的概念应该是大多数Web开发人员熟悉的。 这是一组DOM元素,在整个应用程序的视图中被重用。 在HTML5标准引入了模板标签之前,大多数模板都被包含在script标签中。

<script id="tpl" type="text/template"><span>I am span in template</span>
</script>

这种方法当然有许多缺点,如语义和手动创建DOM模型的必要性。 使用模板标签浏览器解析HTML并创建DOM树,但不呈现它。 然后可以通过内容属性访问:

<script>let tpl = document.querySelector('#tpl');let container = document.querySelector('.insert-after-me');insertAfter(container, tpl.content);
</script>
<div class="insert-after-me"></div>
<ng-template id="tpl"><span>I am span in template</span>
</ng-template>

Angular支持这种方法,并实现TemplateRef类来处理模板。 以下是如何使用它:

@Component({selector: 'sample',template: `<ng-template #tpl><span>I am span in template</span></ng-template>`
})
export class SampleComponent implements AfterViewInit {@ViewChild("tpl") tpl: TemplateRef<any>;ngAfterViewInit() {let elementRef = this.tpl.elementRef;// outputs `template bindings={}`console.log(elementRef.nativeElement.textContent);}
}

该框架从DOM中删除模板元素,并在其位置插入注释。 这是呈现时的样子:

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

TemplateRef类本身是一个简单的类。 它的elementRef属性拥有对其宿主元素的引用,并具有一个方法createEmbeddedView。 这个方法非常有用,因为它允许我们创建一个视图并以ViewRef的形式返回一个引用。

ViewRef

这种抽象表示Angular视图。 在Angular世界中,View是应用程序UI的基本构建块。 它是创造和消灭的最小的元素分组。 Angular哲学鼓励开发人员将UI视为Views的组合,而不是将其视为独立的HTML标签。

Angular支持两种类型的视图:

  • 嵌入视图链接到模板
  • 链接到组件的主机视图

创建嵌入的视图

一个模板只是一个视图的蓝图。 一个视图可以使用前面提到的createEmbeddedView方法从模板实例化,如下所示:

ngAfterViewInit() {let view = this.tpl.createEmbeddedView(null);
}

创建宿主视图

宿主视图是在组件动态实例化时创建的。 可以使用ComponentFactoryResolver动态创建一个组件:

constructor(private injector: Injector,private r: ComponentFactoryResolver) {let factory = this.r.resolveComponentFactory(ColorComponent);let componentRef = factory.create(injector);let view = componentRef.hostView;
}

在Angular中,每个组件都绑定到一个注入器的特定实例,所以我们在创建组件时传递当前的注入器实例。 此外,不要忘记,动态实例化的组件必须添加到模块或主机组件的EntryComponents。

所以,我们已经看到如何创建嵌入和宿主视图。 一旦创建了视图,就可以使用ViewContainer将其插入到DOM中。 下一节将探讨其功能。

ViewContainerRef

表示可以附加一个或多个视图的容器。

首先要提到的是,任何DOM元素都可以用作视图容器。有趣的是,Angular不在元素内插入视图,而是在绑定到ViewContainer的元素之后附加它们。 这与路由器插座如何插入组件类似。

通常,标记应该创建ViewContainer的地方的好候选者是ng-container元素。 它被渲染为一个注释,所以它不会在DOM中引入多余的html元素。 以下是在组件模板的特定位置创建ViewContainer的示例:

@Component({selector: 'sample',template: `<span>I am first span</span><ng-container #vc></ng-container><span>I am last span</span>`
})
export class SampleComponent implements AfterViewInit {@ViewChild("vc", {read: ViewContainerRef}) vc: ViewContainerRef;ngAfterViewInit(): void {// outputs `template bindings={}`console.log(this.vc.element.nativeElement.textContent);}
}

就像其他DOM抽象一样,ViewContainer绑定到通过元素属性访问的特定DOM元素。 在这个例子中,ng-container元素被绑定为注释的示例中,输出为template bindings = {}。

Manipulating views

ViewContainer为操作视图提供了一个方便的API:

class ViewContainerRef {...clear() : voidinsert(viewRef: ViewRef, index?: number) : ViewRefget(index: number) : ViewRefindexOf(viewRef: ViewRef) : numberdetach(index?: number) : ViewRefmove(viewRef: ViewRef, currentIndex: number) : ViewRef
}

我们之前已经看到,如何从模板和组件手动创建两种类型的视图。 一旦我们有了一个视图,我们可以使用插入方法将其插入到DOM中。 所以,下面是从模板中创建一个嵌入式视图并将其插入到由ng-container元素标记的特定位置的示例:

@Component({selector: 'sample',template: `<span>I am first span</span><ng-container #vc></ng-container><span>I am last span</span><ng-template #tpl><span>I am span in template</span></ng-template>`
})
export class SampleComponent implements AfterViewInit {@ViewChild("vc", {read: ViewContainerRef}) vc: ViewContainerRef;@ViewChild("tpl") tpl: TemplateRef<any>;ngAfterViewInit() {let view = this.tpl.createEmbeddedView(null);this.vc.insert(view);}
}

通过这个实现,生成的html看起来像这样:

<sample><span>I am first span</span><!--template bindings={}--><span>I am span in template</span><span>I am last span</span><!--template bindings={}-->
</sample>

要从DOM中删除视图,我们可以使用detach方法。 所有其他方法都是自解释性的,可用于通过索引获取对视图的引用,将视图移至其他位置或从容器中移除所有视图。

Creating Views

ViewContainer还提供API来自动创建视图:

class ViewContainerRef {element: ElementReflength: numbercreateComponent(componentFactory...): ComponentRef<C>createEmbeddedView(templateRef...): EmbeddedViewRef<C>...
}

这些都是我们上面手动完成的简单包装。 他们从模板或组件创建一个视图,并将其插入到指定位置。

ngTemplateOutlet and ngComponentOutlet

ngTemplateOutlet

这个将一个DOM元素标记为ViewContainer,并在其中插入一个由模板创建的嵌入视图,而不需要在组件类中明确地做到这一点。 这意味着上面我们创建视图并将其插入到#vc DOM元素的示例可以像这样重写:

@Component({selector: 'sample',template: `<span>I am first span</span><ng-container [ngTemplateOutlet]="tpl"></ng-container><span>I am last span</span><ng-template #tpl><span>I am span in template</span></ng-template>`
})
export class SampleComponent {}

正如你所看到的,我们不使用任何视图实例化组件类中的代码。 非常便利。

ngComponentOutlet

该指令类似于ngTemplateOutlet,不同之处在于它创建一个宿主视图(实例化一个组件),而不是嵌入视图。 你可以像这样使用它:

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

总结

现在,所有这些信息似乎都可以被消化,但实际上这些信息是非常连贯的,并且通过视图来显示操纵DOM的清晰模型。 通过使用ViewChild查询和模板变量引用,您可以获得对Angular DOM抽象的引用。 围绕DOM元素的最简单的包装是ElementRef。 对于具有TemplateRef的模板,您可以创建嵌入式视图。 主机视图可以在使用ComponentFactoryResolver创建的componentRef上访问。 视图可以用ViewContainerRef来操作。 有两个使自动手动过程的指令:ngTemplateOutlet - 用于嵌入视图,ngComponentOutlet用于宿主视图(动态组件)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/391486.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

我如何预测10场英超联赛的确切结果

Is there a way to predict the outcome of any soccer game with 100% accuracy? The honest and simplest answer is…. no. Regardless of what your fantasy football friends say, there is absolutely no way to be 100% certain, but there is a proven, mathematical …

深度学习数据自动编码器_如何学习数据科学编码

深度学习数据自动编码器意见 (Opinion) When I first wanted to learn programming, I coded along to a 4 hour long YouTube tutorial.刚开始学习编程时&#xff0c;我编写了长达4个小时的YouTube教程。 “Great,” I thought after finishing the course. “I know how to …

Angular 5.0 学习2:Angular 5.0 开发环境的搭建和新建第一个ng5项目

1.安装Node.js 在开始工作之前&#xff0c;我们必须设置好开发环境。如果你的机器上还没有Node.js和npm&#xff0c;请先安装它们。去Node.js的官网&#xff0c;https://nodejs.org/en/&#xff0c;点击下载按钮&#xff0c;下载最新版本&#xff0c;直接下一步下一步安装即可&…

robot:根据条件主动判定用例失败或者通过

场景&#xff1a; 当用例中的断言部分需要满足特定条件时才会执行&#xff0c;如果不满足条件时&#xff0c;可以主动判定该用例为passed状态&#xff0c;忽略下面的断言语句。 如上图场景&#xff0c;当每月1号时&#xff0c;表中才会生成上月数据&#xff0c;生成后数据不会再…

图深度学习-第1部分

有关深层学习的FAU讲义 (FAU LECTURE NOTES ON DEEP LEARNING) These are the lecture notes for FAU’s YouTube Lecture “Deep Learning”. This is a full transcript of the lecture video & matching slides. We hope, you enjoy this as much as the videos. Of cou…

Git上传项目到github

2019独角兽企业重金招聘Python工程师标准>>> Git入门 个人理解git就是一个上传工具&#xff0c;同时兼具和svn一样的版本控制功能&#xff08;此解释纯属本人个人观点&#xff09; Github是什么 github就是一个分布式版本管理系统&#xff08;反正我就是这么认为的…

robot:当用例失败时执行关键字(发送短信)

使用场景&#xff1a; 当用例失败时需要通知对应人员&#xff0c;则需要在Teardown中&#xff0c;使用关键字Run Keyword If Test Failed Send Message关键字为自定义关键字&#xff0c;${content}为短信内容&#xff0c;${msg_receiver}为短信接收者列表。 当然执行成功时需要…

项目经济规模的估算方法_估算英国退欧的经济影响

项目经济规模的估算方法On June 23 2016, the United Kingdom narrowly voted in a country-wide referendum to leave the European Union (EU). Economists at the time warned of economic losses; the Bank of England produced estimates that that GDP could be as much …

奇迹网站可视化排行榜]_外观可视化奇迹

奇迹网站可视化排行榜]When reading a visualization is what we see really what we get?阅读可视化内容时&#xff0c;我们真正看到的是什么&#xff1f; This post summarizes and accompanies our paper “Surfacing Visualization Mirages” that was presented at CHI …

机器学习 量子_量子机器学习:神经网络学习

机器学习 量子My last articles tackled Bayes nets on quantum computers (read it here!), and k-means clustering, our first steps into the weird and wonderful world of quantum machine learning.我的最后一篇文章讨论了量子计算机上的贝叶斯网络( 在这里阅读&#xf…

BZOJ 1176: [Balkan2007]Mokia

一道CDQ分治的模板题&#xff0c;然而我De了一上午Bug...... 按时间分成左右两半&#xff0c;按x坐标排序然后把y坐标丢到树状数组里&#xff0c;扫一遍遇到左边的就add,遇到右边的query 几个弱智出了bug的点&#xff0c; 一是先分了左右两半再排序&#xff0c;保证的是这次的左…

深入理解InnoDB(1)—行的存储结构

1.InnoDB页的简介 页&#xff08;Page&#xff09;是 Innodb 存储引擎用于管理数据的最小磁盘单位。常见的页类型有数据页、Undo 页、系统页、事务数据页等 2.InnoDB行的存储格式 我们插入MySQL的记录在InnoDB中可能以4中行格式存储&#xff0c;分别是Compact、Redundant、D…

boltzmann_推荐系统系列第7部分:用于协同过滤的Boltzmann机器的3个变体

boltzmannRecSys系列 (RecSys Series) Update: This article is part of a series where I explore recommendation systems in academia and industry. Check out the full series: Part 1, Part 2, Part 3, Part 4, Part 5, Part 6, and Part 7.更新&#xff1a; 本文是我探索…

深入理解InnoDB(2)—页的存储结构

1. 记录头信息 上一篇博客说到每行记录都会有记录头信息&#xff0c;用来记录每一行的一些属性 Compact行记录的记录头信息为例 1.1 delete_mask 这个属性标记着当前记录是否被删除&#xff0c;占用1个二进制位&#xff0c;值为0的时候代表记录并没有被删除&#xff0c;为1的…

爬虫神经网络_股市筛选和分析:在投资中使用网络爬虫,神经网络和回归分析...

爬虫神经网络与AI交易 (Trading with AI) Stock markets tend to react very quickly to a variety of factors such as news, earnings reports, etc. While it may be prudent to develop trading strategies based on fundamental data, the rapid changes in the stock mar…

深入理解InnoDB(3)—索引的存储结构

1. 索引的各种存储结构及其优缺点 1.1 二叉树 优点&#xff1a; 二叉树是一种比顺序结构更加高效地查找目标元素的结构&#xff0c;它可以从第一个父节点开始跟目标元素值比较&#xff0c;如果相等则返回当前节点&#xff0c;如果目标元素值小于当前节点&#xff0c;则移动到左…

深入理解InnoDB(4)—索引使用

1. 索引的代价 在了解索引的代价之前&#xff0c;需要再次回顾一下索引的数据结构B树 如上图&#xff0c;是一颗b树&#xff0c;关于b树的定义可以参见B树&#xff0c;这里只说一些重点&#xff0c;浅蓝色的块我们称之为一个磁盘块&#xff0c;可以看到每个磁盘块包含几个数据…

双城记s001_双城记! (使用数据讲故事)

双城记s001Keywords: Data science, Machine learning, Python, Web scraping, Foursquare关键字&#xff1a;数据科学&#xff0c;机器学习&#xff0c;Python&#xff0c;Web抓取&#xff0c;Foursquare https://br.pinterest.com/pin/92816442292506979/https://br.pintere…

web前端面试总结

2019独角兽企业重金招聘Python工程师标准>>> 摘要&#xff1a;前端的东西特别多&#xff0c;面试的时候我们如何从容应对&#xff0c;作为一个老兵&#xff0c;我在这里分享几点我的经验。 一、javascript 基础(es5) 1、原型&#xff1a;这里可以谈很多&#xff0c;…

tableau破解方法_使用Tableau浏览Netflix内容的简单方法

tableau破解方法Are you struggling to perform EDA with R and Python?? Here is an easy way to do exploratory data analysis using Tableau.您是否正在努力使用R和Python执行EDA&#xff1f; 这是使用Tableau进行探索性数据分析的简单方法。 Lets Dive in to know the …