freeCodeCamp/guide/chinese/angular/views/index.md

243 lines
14 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
title: Views
localeTitle: 查看
---
# 查看
#### 动机
视图提供了必要的抽象层。他们让Angular独立于平台特定的实用程序。作为一种跨平台技术Angular使用其视图与平台连接。
对于Angular模板HTML中的每个元素都有一个相应的视图。 Angular建议通过这些视图与平台进行交互。虽然直接操纵仍然可行但Angular警告它。 Angular提供了自己的应用程序编程接口API来替换本机操作。
对特定于平台的API的回避视图会产生影响。在Web浏览器中开发Angular时元素存在于两个位置DOM和视图。仅与DOM混淆不会影响视图。
由于Angular不与平台接口因此会产生不连续性。视图应该一对一地镜像平台。否则Angular会浪费资源管理与其不匹配的元素。如果删除元素这很糟糕。
这些差异使得视图显得不必要。永远不要忘记Angular是一个普遍的开发平台。视图是此目的的必要抽象。
通过遵守视图Angular应用程序将在所有受支持的开发平台上运行。平台包括WebAndroid和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]` 。它产生以下结果。
```typescript
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`一些帮助器和引用提供了其他所需的实用程序。以下示例将所有内容放在一起。
```typescript
// another.component.ts
import { Component } from '@angular/core';
@Component({
template: `
<h1>Another Component Content</h1>
<h3>Dynamically Generated!</h3>
`
})
export class AnotherComponent { }
```
```typescript
// 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`元数据必须包含用于[引导的](https://angular.io/guide/bootstrapping) 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的连接。
还有其他方法可以从组件动态创建主机视图。其他方式往往[侧重于优化](https://blog.angularindepth.com/working-with-dom-in-angular-unexpected-consequences-and-optimization-techniques-682ac09f6866) 。
`ViewContainerRef`拥有强大的API。它可以管理托管或嵌入其视图中的任意数量的视图。 API包括视图操作例如插入移动和删除。这允许您通过Angular的视图模型操作DOM。这是最佳实践以便Angular和DOM相互匹配。
#### 嵌入式视图
注意嵌入视图附加到其他视图没有添加输入。主机视图使用来自其主机视图的输入数据附加到DOM元素并将其描述为组件。
结构指令创建[围绕一大块HTML内容](https://angular.io/guide/structural-directives#the-asterisk--prefix)的[`ng-template`](https://angular.io/guide/structural-directives#the-asterisk--prefix) 。该指令的host元素附加了一个视图容器。这使得内容可以有条件地呈现为其预期的布局。
`ng-template`保存嵌入视图节点表示其innerHTML中的每个元素。 `ng-template`绝不是DOM元素。它自我评论。标签定义其嵌入视图的扩展。
#### 嵌入式视图续
实例化嵌入式视图不需要超出其自身引用的外部资源。 `@ViewChild`查询可以获取它。
使用模板引用,从中调用`createEmbeddedView`就可以了。引用的innerHTML成为其自己的嵌入式视图实例。
在下一个示例中, `<ng-container></ng-container>`是一个视图容器。 `ng-container`在编译期间被注释掉,就像`ng-template` 。因此它提供了用于插入嵌入视图同时保持DOM倾斜的出口。
嵌入视图模板将插入`ng-container`的布局位置。除了视图容器之外,这个新插入的视图没有额外的视图封装。请记住它与主机视图的不同之处(主机视图附加到其`ng-component`元素包装器)。
```typescript
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` 。这提供类型的模板参考`TemplateRef` 。 `TemplateRef`包含`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提供的观点。这将在所有平台上产生可预测的结果。
查看以下资源!本文仅涉及表面。对于一篇文章,视图还有很多其他用例。
## 来源
* [AngularInDepth.com。 “组件视图主机视图嵌入式视图”40423772。 2017年7月11日。“视图主机视图和嵌入视图之间有什么区别”](https://stackoverflow.com/questions/40423772/what-is-the-difference-between-a-view-a-host-view-and-an-embedded-view)
* [角度团队。 “结构指令”。 _谷歌_ 2018年5月31日访问](https://angular.io/guide/structural-directives)
* [KoretskyiMaxim。 “使用ViewContainerRef探索Angular DOM操作技术”。 _Angular In Depth_ 2017年3月4日。访问2018年5月30日。](https://blog.angularindepth.com/exploring-angular-dom-abstractions-80b3ebcfc02)
* [KoretskyiMaxim。 “实现高级DOM操作方案”。 _Youtube_ 由ng-conf上传2018年4月19日。访问2018年5月30日](https://www.youtube.com/watch?v=vz9cNCkaPsY)
* [KoretskyiMaxim。 “在Angular中使用DOM意外后果和优化技术”。 _Angular In Depth_ 2017年5月3日。访问2018年5月31日](https://blog.angularindepth.com/working-with-dom-in-angular-unexpected-consequences-and-optimization-techniques-682ac09f6866)
## 资源
* [角度文档](https://angular.io/guide/pipes)
* [角度深度](https://blog.angularindepth.com)
* [ViewContainerRef](https://angular.io/api/core/ViewContainerRef)
* [TemplateRef](https://angular.io/api/core/TemplateRef)
* [Angular GitHub存储库](https://github.com/angular/angular)
* [角度CLI](https://cli.angular.io)