我们很高兴地宣布 Angular v14 发布!从类型化表单和独立组件到 Angular CDK(组件开发工具包)中的新原语,我们很高兴分享每个功能如何使 Angular 变得更强大。
自上一个版本以来,我们完成了两项主要的征求意见请求 (RFC),这为整个 Angular 社区提供了针对提议的更改提供设计反馈的机会。因此,我们的严格类型反应表单 RFC解决了我们的#1 GitHub 问题,并且我们的独立 API RFC引入了一种更简单的方法来编写 Angular 应用程序。
我们还将 Angular 组织中存储库中的默认分支重命名为main
,以履行我们对包容性社区的承诺。
此外,此版本还包括由社区成员直接贡献的许多功能和错误修复,从添加路由器强类型到更多可树摇动的错误消息。我们很高兴强调 RFC 和社区如何继续使 Angular 成为更好的默认开发人员体验!
Angular 独立组件旨在通过减少对 NgModule 的需求来简化 Angular 应用程序的编写。在 v14 中,独立组件处于开发者预览版中。它们已准备好在您的应用程序中用于探索和开发,但不是稳定的 API,并且可能会在我们典型的向后兼容性模型之外发生变化。
// main.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common'; // includes NgIf and TitleCasePipe
import { bootstrapApplication } from '@angular/platform-browser';
import { MatCardModule } from '@angular/material/card';
import { ImageComponent } from './app/image.component';
import { HighlightDirective } from './app/highlight.directive';
@Component({
selector: 'app-root',
standalone: true,
imports: [
ImageComponent, HighlightDirective, // import standalone Components, Directives and Pipes
CommonModule, MatCardModule // and NgModules
],
template: `
<mat-card *ngIf="url">
<app-image-component [url]="url"></app-image-component>
<h2 app-highlight>{{name | titlecase}}</h2>
</mat-card>
`
})
export class ExampleStandaloneComponent {
name = "emma";
url = "www.emma.org/image";
}
// Bootstrap a new Angular application using our `ExampleStandaloneComponent` as a root component.
bootstrapApplication(ExampleStandaloneComponent);
对于独立组件、指令和管道, standalone: true
标志允许您直接在@Component()
中添加imports
,而无需@NgModule()
。
探索新的 Stackblitz 演示应用程序,了解有关如何使用独立组件构建 Angular 应用程序的更多信息。
在开发者预览版中,我们希望利用开源来确保独立版做好充分准备,可以作为稳定的 API 发布。添加您的第一个独立组件 ng generate component <name> --standalone
,然后前往我们的 GitHub 存储库,在您开始探索时提供反馈。
在接下来的几个月中,我们将继续构建原理图(例如 ng new <app-name> --standalone
),以及记录这种新的、简化的心智模型的用例和学习旅程。请记住,在开发者预览中,随着我们继续实施我们的设计,可能会发生变化。
您可以在两个 RFC 和公共设计审查中阅读有关当前实现背后的设计思想的更多信息。请务必在此处和Twitter上关注我们,以获取独立 API 的未来更新。
Angular v14 解决了 Angular 的GitHub 顶级问题:为 Angular Reactive Forms 包实现严格类型。
类型化表单确保表单控件、组和数组内的值在整个 API 表面上都是类型安全的。这使得表单更安全,特别是对于深度嵌套的复杂情况。
const cat = new FormGroup({
name: new FormGroup({
first: new FormControl('Barb'),
last: new FormControl('Smith'),
}),
lives: new FormControl(9),
});
// Type-checking for forms values!
// TS Error: Property 'substring' does not exist on type 'number'.
let remainingLives = cat.value.lives.substring(1);
// Optional and required controls are enforced!
// TS Error: No overload matches this call.
cat.removeControl('lives');
// FormGroups are aware of their child controls.
// name.middle is never on cat
let catMiddleName = cat.get('name.middle');
此功能是公开征求意见和设计审查的结果,它建立在 Angular 社区贡献者(包括Sonu Kapoor 、 Netanel Basel和Cédric Exbrayat)之前的原型设计、工作和测试的基础上。
更新原理图允许增量迁移到键入的表单,因此您可以逐步向现有表单添加键入内容,并具有完全向后兼容性。 ng update
将用非类型化版本替换所有表单类(例如, FormGroup
-> UntypedFormGroup
)。然后,您可以按照自己的节奏启用类型(例如, UntypedFormGroup
-> FormGroup
)。
// v13 untyped form
const cat = new FormGroup({
name: new FormGroup(
first: new FormControl('Barb'),
last: new FormControl('Smith'),
),
lives: new FormControl(9)
});
// v14 untyped form after running `ng update`
const cat = new UntypedFormGroup({
name: new UntypedFormGroup(
first: new UntypedFormControl('Barb'),
last: new UntypedFormControl('Smith'),
),
lives: new UntypedFormControl(9)
});
为了利用新的类型支持,我们建议搜索Untyped
表单控件并尽可能迁移到新类型的表单 API 界面。
// v14 partial typed form, migrating `UntypedFormGroup` -> `FormGroup`
const cat = new FormGroup({
name: new FormGroup(
first: new UntypedFormControl('Barb'),
last: new UntypedFormControl('Smith'),
),
lives: new UntypedFormControl(9)
});
我们建议新应用程序使用Form*
类,除非该类有意为非类型化(例如,同时具有数字和字符串的FormArray
)。在文档中了解更多信息。
Angular v14 带来了内置功能,使开发人员能够构建高质量的应用程序,从路由到代码编辑器,从angular.io 上的新更改检测指南开始。
另一个最佳实践是确保应用程序的页面标题能够唯一地传达页面的内容。 v13.2 通过 Angular Router 中新的Route.title
属性简化了这一过程。由于Marko Stanimirović做出了惊人的社区贡献,现在添加title
不需要额外的导入,并且是强类型的。
// app-routing.module.ts
const routes: Routes = [{
path: 'home',
component: HomeComponent
title: 'My App - Home' // <-- Page title
}, {
path: 'about',
component: AboutComponent,
title: 'My App - About Me' // <-- Page title
}];
您可以通过提供自定义TitleStrategy
来配置更复杂的标题逻辑。
// app-routing.module.ts
const routes: Routes = [{
path: 'home',
component: HomeComponent
}, {
path: 'about',
component: AboutComponent,
title: 'About Me' // <-- Page title
}];
@Injectable()
export class TemplatePageTitleStrategy extends TitleStrategy {
override updateTitle(routerState: RouterStateSnapshot) {
const title = this.buildTitle(routerState);
if (title !== undefined) {
document.title = `My App - ${title}`;
} else {
document.title = `My App - Home`;
};
};
@NgModule({
…
providers: [{provide: TitleStrategy, useClass: TemplatePageTitleStrategy}]
})
class MainModule {}
在这些示例中,导航到“/about”会将文档标题设置为“我的应用程序 – 关于我”,而导航到“/home”会将文档标题设置为“我的应用程序 – 主页”。
您可以在 Google I/O 2022 研讨会上了解有关使用 Angular 进行无障碍构建的更多信息。
视频:在 Google I/O 2022 上学习使用 Angular 进行无障碍构建
新的扩展诊断提供了一个可扩展的框架,使您可以更深入地了解模板以及如何改进它们。诊断为您的模板提供编译时警告和精确、可操作的建议,在运行时之前捕获错误。
我们对它为开发人员在未来添加诊断功能引入的灵活框架感到兴奋。
在 v13.2 中,我们包含内置的扩展诊断功能,以帮助开发人员捕获两个最常见的模板错误。
捕获双向数据绑定上的无效“Banana in a box”错误
开发人员常见的语法错误是在双向绑定中翻转方括号和圆括号,编写([])
而不是[()]
。由于()
sorta 看起来像香蕉,而[]
sorta 看起来像盒子,因此我们将其昵称为“盒子里的香蕉”错误,因为香蕉应该放在盒子里。
虽然此错误在技术上是有效的语法,但我们的 CLI 可以认识到这很少不是开发人员想要的。在 v13.2 版本中,我们引入了有关此错误的详细消息传递以及有关如何解决此问题的指南,所有这些都在 CLI 和代码编辑器中进行。
Warning: src/app/app.component.ts:7:25 - warning NG8101: In the two-way binding syntax the parentheses should be inside the brackets, ex. '[(fruit)]="favoriteFruit"'.
Find more at https://angular.io/guide/two-way-binding
7 <app-favorite-fruit ([fruit])="favoriteFruit"></app-favorite-fruit>
捕获不可空值的空值合并
扩展诊断还会引发 Angular 模板中无用的无效合并运算符(??) 的错误。具体来说,当输入不是“可空”时会引发此错误,这意味着其类型不包括null
或undefined
。
扩展诊断在ng build
、 ng serve
期间以及使用 Angular 语言服务实时显示为警告。诊断可在tsconfig.json
中进行配置,您可以在其中指定诊断是否应该是warning
、 error
或suppress
。
// sconfig.json
{
"angularCompilerOptions": {
"extendedDiagnostics": {
// The categories to use for specific diagnostics.
"checks": {
// Maps check name to its category.
"invalidBananaInBox": "error"
"nullishCoalescingNotNullable": "warning"
},
// The category to use for any diagnostics not listed in `checks` above.
"defaultCategory": "suppress"
},
...
},
...
}
在我们的文档和扩展诊断博客文章中了解有关扩展诊断的更多信息。
当我们继续编写Angular 调试指南时, Ramesh Thiruchelvam的社区贡献添加了新的运行时错误代码。强大的错误代码使您可以更轻松地参考和查找有关如何调试错误的信息。
这允许构建优化器从生产包中摇树错误消息(长字符串),同时保留错误代码。
@Component({...})
class MyComponent {}
@Directive({...})
class MyDirective extends MyComponent {} // throws an error at runtime
// Before v14 the error is a string:
> Directives cannot inherit Components. Directive MyDirective is attempting to extend component MyComponent.
// Since v14 the error code makes this tree-shakeable:
> NG0903: Directives cannot inherit Components. Directive MyDirective is attempting to extend component MyComponent.
// v14 production bundles preserve the error code, tree-shaking strings and making the bundle XX smaller:
> NG0903
要调试生产错误,我们建议前往参考指南并在开发环境中重现错误,以便查看完整的字符串。我们将继续逐步重构现有错误,以便在未来版本中利用这种新格式。
v14 包括对最新 TypeScript 4.7 版本的支持,现在默认以 ES2020 为目标,这使得 CLI 可以在不降级的情况下发布更小的代码。
此外,我们还想强调三个特色:
绑定到受保护的组件成员
在 v14 中,您现在可以直接从模板绑定到受保护的组件成员,这要感谢Zack Elliott的贡献!
@Component({
selector: 'my-component',
template: '{{ message }}', // Now compiles!
})
export class MyComponent {
protected message: string = 'Hello world';
}
这使您可以更好地控制可重用组件的公共 API 界面。
嵌入式视图中的可选注入器
v14 添加了对在创建嵌入视图时传入可选注入器的支持 ViewContainerRef.createEmbeddedView
和 TemplateRef.createEmbeddedView
。注入器允许在特定模板内自定义依赖注入行为。
这使得能够使用更清晰的 API 来编写可重用组件以及 Angular CDK 中的组件原语。
viewContainer.createEmbeddedView(templateRef, context, {
injector: injector,
})
NgModel OnPush 模型
最后, Artur Androsovych的社区贡献解决了一个首要问题,并确保 NgModel 更改反映在 OnPush 组件的 UI 中。
@Component({
selector: 'my-component',
template: `
<child [ngModel]="value"></child>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
class MyComponent {}
CDK 和工具改进为更强大的开发环境提供了构建块,从 CDK 菜单原语到 CLI 自动完成。
Angular 的组件开发工具包提供了一整套用于构建 Angular 组件的工具。在 v14 中,我们将 CDK菜单和对话框提升至稳定!
此版本包括新的 CDK 原语,可用于基于WAI-ARIA 菜单和菜单栏设计模式创建更易于访问的自定义组件。
<ul cdkTargetMenuAim cdkMenuBar>
<li cdkMenuItem
[cdkMenuTriggerFor]="file">
File
</li>
</ul>
<ng-template #file>
<ul cdkMenu cdkTargetMenuAim>
<li cdkMenuItem>Open</li>
</ul>
</ng-template>
hasHarness
和 getHarnessOrNullv14 包括HarnessLoader
的新方法,用于检查线束是否存在,并返回线束实例(如果存在)。组件测试工具继续提供一种灵活的方式来为组件编写更好的测试。
const loader = TestbedHarnessEnvironment
.loader(fixture);
const hasButton = await loader
.hasHarness(MatButtonHarness)
const buttonHarnessOrNull = await loader
.getHarnessOrNull(MatButtonHarness);
标准化的 CLI 参数解析意味着整个 Angular CLI 的一致性更高,现在每个标志都使用--lower-skewer-case
格式。我们删除了已弃用的驼峰式大小写参数支持,并添加了对组合别名使用的支持。
好奇这意味着什么?运行ng --help
以获得更清晰的输出,解释您的选项。
ng completion
不小心输入ng sevre
而不是ng serve
情况时有发生。拼写错误是命令行提示符引发错误的最常见原因之一。为了解决这个问题,v14 的新ng completion
引入了实时提前输入自动补全!
为了确保所有 Angular 开发人员都了解这一点,CLI 将提示您在 v14 中的第一次命令执行期间选择自动完成。您还可以手动运行ng completion
,CLI 会自动为您进行设置。
ng analytics
CLI 的分析命令允许您控制分析设置和打印分析信息。更详细的输出可以清楚地传达您的分析配置,并为我们的团队提供遥测数据,以告知我们的项目优先级。当您打开它时,它会非常有帮助!
ng cache
ng cache
提供了一种从命令行控制和打印缓存信息的方法。您可以启用、禁用或从磁盘删除,以及打印统计信息和信息。
感谢Keith Li的社区贡献,Angular DevTools 调试扩展现在支持离线使用。对于 Firefox 用户,请在Mozilla 附加组件中找到该扩展。
最后,v14 为ng build
引入了一个基于 esbuild 的实验性构建系统,该系统编译纯 ESM 输出。
要在您的应用程序中尝试此操作,请更新angular.json
中的浏览器构建器:
// angular.json
"builder": "@angular-devkit/build-angular:browser"
"builder": "@angular-devkit/build-angular:browser-esbuild"
随着我们不断增加对 Sass 等样式表预处理器的支持,我们的团队很高兴收集有关您应用程序性能的反馈。
随着此版本的发布,我们还更新了公共路线图,以反映当前和未来团队项目和探索的状态。
您可以在 Angular 博客和#GoogleIO 的 State of Angular 中了解有关我们团队未来计划的更多信息:Google I/O 2022 上的 Angular 现状
感谢所有每天构建、创新和激励我们的出色开发人员 — 我们对 Angular 的发展方向感到兴奋!
前往update.angular.io并通过@Angular向我们发送推文,了解您的#ngUpdate体验!
原文链接:https://blog.angular.dev/angular-v14-is-now-available-391a6db736af