Angular 视图


动机

视图提供了必要的抽象层。他们让Angular独立于平台特定的实用程序。作为一种跨平台技术,Angular使用其视图与平台连接。

对于Angular模板HTML中的每个元素,都有一个相应的视图。 Angular建议通过这些视图与平台进行交互。虽然直接操纵仍然可行,但Angular警告它。 Angular提供了自己的应用程序编程接口(API)来替换本机操作。

对特定于平台的API的回避视图会产生影响。在Web浏览器中开发Angular时,元素存在于两个位置:DOM和视图。仅与DOM混淆不会影响视图。

由于Angular不与平台接口,因此会产生不连续性。视图应该一对一地镜像平台。否则,Angular会浪费资源管理与其不匹配的元素。如果删除元素,这很糟糕。

这些差异使得视图显得不必要。永远不要忘记Angular是一个普遍的开发平台。视图是此目的的必要抽象。

通过遵守视图,Angular应用程序将在所有受支持的开发平台上运行。平台包括Web,Android和Apple iOS。

注意

从这里开始,本文假设一个Web浏览器环境。您可以通过更适用于您首选平台的内容轻松替换DOM。

观点是什么?

视图几乎就像他们自己的虚拟DOM。每个视图都包含对DOM的相应部分的引用。视图内部是反映本节内容的节点。 Angular为每个DOM元素分配一个视图节点。每个节点都包含对匹配元素的引用。

当Angular检查更改时,它会检查视图。 Angular避免了引擎盖下的DOM。视图代表它引用DOM。还有其他机制来确保视图更改呈现给DOM。相反,对DOM的更改不会影响视图。

同样,除了DOM之外,视图在所有开发平台上都很常见。即使开发一个平台,视图仍被视为最佳实践。他们保证Angular对DOM有正确的解释。

第三方库可能不存在视图。对于这种情况,直接DOM操作是一个逃避的方法。当然,不要指望应用程序跨平台运行。

视图类型

有两种主要类型的视图:嵌入式和主机式。

还存在视图容器。它们包含嵌入式和主机视图,通常称为简单的“视图”。

每个@Component类都使用Angular注册一个视图容器(视图)。新组件生成针对特定DOM元素的自定义选择器。无论出现在何处,视图都会附加到该元素。 Angular现在知道组件存在于查看视图模型。

主机视图附加到使用工厂动态创建的组件。工厂提供了视图实例化的蓝图。这样,应用程序可以在运行时实例化组件的主机视图。主机视图在其实例化时附加到组件的包装器。该视图存储描述传统组件功能的数据。

<ng-template></ng-template>类似于HTML5 <template></template>元素。 Angular的ng-template适用于嵌入式视图。与主机视图不同,这些视图不会附加到DOM元素。它们与主机视图相同,因为它们都存在于视图容器内。

请记住, ng-template不是DOM元素。它后来被注释掉了,只留下了嵌入式视图节点。

差异取决于输入数据;嵌入式视图不存储组件数据。它们将一系列元素存储为包含其模板的节点。该模板构成了ng-template所有innerHTML。嵌入视图中的每个元素都是其自己的单独视图节点。

主机视图和容器

主机视图_托管_动态组件。视图容器(视图)自动附加到模板中已有的元素。视图可以附加到除组件类特有的元素之外的任何元素。

想想传统的组件生成方法。它首先创建一个类,用@Component装饰,然后填充元数据。对于模板的任何预定义组件元素,都会出现此方法。

尝试使用Angular命令行界面(CLI)命令: ng generate component [name-of-component] 。它产生以下结果。

import { Component, OnInit } from '@angular/core';

 @Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.css']
 })
 export class ExampleComponent implements OnInit {
  constructor() { }

  ngOnInit() { }
 }

这将使用选择器app-example创建组件。这会将视图容器附加到模板中的<app-example></app-example> 。如果这是应用程序的根,则其视图将封装所有其他视图。根视图标志着Angular的应用程序的开始。

动态创建组件并在Angular视图模型中注册它们需要一些额外的步骤。结构指令有助于管理动态内容( *ngIf, *ngFor, and *ngSwitch… )。但是,指令不能扩展到更大的应用程序。太多的结构指令使模板复杂化。

这是从现有类逻辑实例化组件派上用场的地方。这些组件需要创建可以插入视图模型的主机视图。主机视图保存组件的数据,以便Angular识别其结构目的。

主机视图续

每个组件都有一个类定义。然而,JavaScript不支持类。类是语法糖。它们生成包含组件工厂的函数。

工厂是主持人观点的蓝图。他们构建视图以代表其组件与Angular交互。主机视图附加到DOM元素。从技术上讲,任何元素都可以,但最常见的目标是<ng-component></ng-component>

必须首先存在用于保存视图的视图容器(视图)。 <ng-container></ng-container>是附加视图容器的好地方。视图容器是同样类型的视图,也适用于模板类元素。

来自@angular/core一些帮助器和引用提供了其他所需的实用程序。以下示例将所有内容放在一起。

// another.component.ts

 import { Component } from '@angular/core';

 @Component({
  template: `
  <h1>Another Component Content</h1>
  <h3>Dynamically Generated!</h3>
  `
 })
 export class AnotherComponent { }
// example.component.ts

 import { AfterViewInit, Component, ViewChild,
 ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
 import { AnotherComponent } from './another.component';

 @Component({
  selector: 'app-example',
  template: `
  <h1>Application Content</h1>
  <ng-container #container></ng-container>
  <h3>End of Application</h3>
  `,
  entryComponents: [ AnotherComponent ]
 })
 export class ExampleComponent implements AfterViewInit {
  @ViewChild("container", { read: ViewContainerRef }) ctr: ViewContainerRef;

  constructor(private resolve: ComponentFactoryResolver) { }

  ngAfterViewInit() {
    const factory = this.resolve.resolveComponentFactory(AnotherComponent);
    this.ctr.createComponent(factory);
  }
 }

假设AnotherComponent和ExampleComponent都在同一模块下声明。 AnotherComponent是一个动态添加到ExampleComponent视图中的简单类组件。 ExampleComponent的entryComponents元数据必须包含用于引导的 AnotherComponent。

虽然ExampleComponent是模板的一部分,但AnotherComponent仍然是分离的。它从ExampleComponent动态渲染到模板中。

存在两个视图容器: <app-example></app-example><ng-container></ng-container> 。此示例的主机视图将插入ng-container

@ViewChild查询完成后,将触发AfterViewInit生命周期钩子。使用模板引用变量 #container @ViewChild@ViewChild引用ng-container作为ctr

ViewContainerRef是视图容器(视图)的引用类型。 ViewContainerRef引用支持插入其他视图的视图。 ViewContainerRef包含更多用于管理其包含视图的方法。

通过依赖注入,构造函数实例化Angular的ComponentFactoryResolver服务的实例。此服务提取AnotherComponent的工厂函数(主机视图蓝图)。

createComponent的单个参数需要工厂。 createComponent函数派生自ViewContainerRef 。它在从组件工厂派生的主机视图下实例化AnotherComponent。

然后主机视图将插入到视图容器中。 <ng-component></ng-component>将组件包装在视图容器内。它附有上述主机视图。 ng-component是主机视图与DOM的连接。

还有其他方法可以从组件动态创建主机视图。其他方式往往侧重于优化 。

ViewContainerRef拥有强大的API。它可以管理托管或嵌入其视图中的任意数量的视图。 API包括视图操作,例如插入,移动和删除。这允许您通过Angular的视图模型操作DOM。这是最佳实践,以便Angular和DOM相互匹配。

嵌入式视图

注意:嵌入视图附加到其他视图没有添加输入。主机视图使用来自其主机视图的输入数据附加到DOM元素,并将其描述为组件。

结构指令创建围绕一大块HTML内的[ng-template 。该指令的host元素附加了一个视图容器。这使得内容可以有条件地呈现为其预期的布局。

ng-template保存嵌入视图节点,表示其innerHTML中的每个元素。 ng-template绝不是DOM元素。它自我评论。标签定义其嵌入视图的扩展。

嵌入式视图续

实例化嵌入式视图不需要超出其自身引用的外部资源。 @ViewChild查询可以获取它。

使用模板引用,从中调用createEmbeddedView就可以了。引用的innerHTML成为其自己的嵌入式视图实例。

在下一个示例中, <ng-container></ng-container>是一个视图容器。 ng-container在编译期间被注释掉,就像ng-template 。因此,它提供了用于插入嵌入视图同时保持DOM倾斜的出口。

嵌入视图模板将插入ng-container的布局位置。除了视图容器之外,这个新插入的视图没有额外的视图封装。请记住它与主机视图的不同之处(主机视图附加到其ng-component元素包装器)。

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

 @Component({
  selector: 'app-example',
  template: `
  <h1>Application Content</h1>
  <ng-container #container></ng-container> <!-- embed view here -->
  <h3>End of Application</h3>

  <ng-template #template>
    <h1>Template Content</h1>
    <h3>Dynamically Generated!</h3>
  </ng-template>
  `
 })
 export class ExampleComponent implements AfterViewInit {
  @ViewChild("template", { read: TemplateRef }) tpl: TemplateRef<any>;
  @ViewChild("container", { read: ViewContainerRef }) ctr: ViewContainerRef;

  constructor() { }

  ngAfterViewInit() {
    const view =  this.tpl.createEmbeddedView(null);
    this.ctr.insert(view);
  }
 }

@ViewChild查询模板引用变量 #template 。这提供类型的模板参考TemplateRefTemplateRef包含createEmbeddedView函数。它将模板实例化为嵌入式视图。

createEmbeddedView的单个参数用于上下文。如果您想传递其他元数据,可以在此处作为对象进行传递。这些字段应与ng-template属性匹配( let-[context-field-key-name]=“value” )。传递null表示不需要额外的元数据。

第二个@ViewChild查询提供对ng-container的引用,作为ViewContainerRef 。嵌入式视图仅附加到其他视图,而不是DOM。 ViewContainerRef引用嵌入视图中的视图。

嵌入式视图也可以插入到<app-example></app-example>的组件视图中。此方法将视图定位在ExampleComponent视图的最后。但是,在这个例子中,我们希望内容显示在ng-container所在的中间位置。

ViewContainerRef insert函数_将_嵌入视图插入ng-container 。视图内容在ExampleComponent视图中间的预期位置显示。

结论

不建议使用特定于平台的方法操作DOM。创建和管理一组紧凑的视图使Angular和DOM保持在同一页面上。更新视图会通知Angular当前DOM的状态。对视图的更新也会延续到DOM显示的内容。

Angular为视图交互提供了灵活的API。由于这种抽象级别,开发独立于平台的应用程序是可能的。当然,依赖于平台的策略的后退诱惑仍然存在。除非你有充分的理由不这样做,否则请坚持使用API​​ Angular提供的观点。这将在所有平台上产生可预测的结果。

查看以下资源!本文仅涉及表面。对于一篇文章,视图还有很多其他用例。

更多Angular教程

学习更多Angular教程