angular-19
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWhen to Use
适用场景
- Implementing Angular components (detail, list, dialog)
- Working with signals, resources, and reactive patterns
- Creating custom pipes or directives
- Setting up dependency injection
- Configuring change detection strategies
Reference files (loaded on demand):
- signals-api.md — Signals, inputs, outputs, model, linkedSignal, signal queries
- resource-api.md — resource(), rxResource(), httpResource()
- template-syntax.md — @let, @if/@for/@switch, @defer, hydration
- 实现Angular组件(详情页、列表页、对话框)
- 使用signals、资源和响应式模式
- 创建自定义管道或指令
- 配置依赖注入
- 设置变更检测策略
参考文件(按需加载):
- signals-api.md — Signals、输入、输出、model、linkedSignal、信号查询
- resource-api.md — resource(), rxResource(), httpResource()
- template-syntax.md — @let、@if/@for/@switch、@defer、hydration
Angular 19 Key Changes
Angular 19 主要变更
Standalone by Default (BREAKING)
默认独立模式(破坏性变更)
All components, directives, and pipes are standalone by default. No needed.
standalone: truetypescript
// ✅ Angular 19: standalone is implicit
@Component({
selector: 'app-example',
templateUrl: './example.component.html',
imports: [CommonModule, MatButtonModule],
})
export class ExampleComponent {}
// ❌ Only if you NEED NgModule (legacy)
@Component({ selector: 'app-legacy', standalone: false })
export class LegacyComponent {}所有组件、指令和管道默认都是独立的,无需设置。
standalone: truetypescript
// ✅ Angular 19:独立模式默认启用
@Component({
selector: 'app-example',
templateUrl: './example.component.html',
imports: [CommonModule, MatButtonModule],
})
export class ExampleComponent {}
// ❌ 仅在需要NgModule时使用(遗留方式)
@Component({ selector: 'app-legacy', standalone: false })
export class LegacyComponent {}Signals (Stable in v19) — Quick Reference
Signals(v19中稳定)——快速参考
typescript
import { signal, computed, effect } from '@angular/core';
count = signal(0); // Writable
doubleCount = computed(() => this.count() * 2); // Derived read-only
this.count.set(5); // Replace
this.count.update(n => n + 1); // Update
const val = this.count(); // Read
// Effect — can set signals directly in v19 (no allowSignalWrites needed)
effect(() => {
console.log('Count:', this.count());
this.logCount.set(this.count()); // ✅ allowed in v19
});For full signal API (inputs, outputs, model, linkedSignal, queries) → see signals-api.md
typescript
import { signal, computed, effect } from '@angular/core';
count = signal(0); // 可写信号
doubleCount = computed(() => this.count() * 2); // 派生只读信号
this.count.set(5); // 替换值
this.count.update(n => n + 1); // 更新值
const val = this.count(); // 读取值
// Effect — v19中可直接在内部设置信号(无需allowSignalWrites)
effect(() => {
console.log('Count:', this.count());
this.logCount.set(this.count()); // ✅ v19中允许
});如需完整的Signal API(输入、输出、model、linkedSignal、查询)→ 查看signals-api.md
Dependency Injection (Modern)
依赖注入(现代方式)
typescript
export class MyComponent {
// ✅ Preferred: inject() function
private readonly http = inject(HttpClient);
private readonly router = inject(Router);
private readonly logger = inject(LoggerService, { optional: true });
private readonly config = inject(CONFIG_TOKEN, { self: true });
}
// Tree-shakable singleton
@Injectable({ providedIn: 'root' })
export class UserService {}
// ✅ New in v19: provideAppInitializer
providers: [
provideAppInitializer(() => {
const config = inject(ConfigService);
return config.load();
}),
]typescript
export class MyComponent {
// ✅ 推荐方式:inject()函数
private readonly http = inject(HttpClient);
private readonly router = inject(Router);
private readonly logger = inject(LoggerService, { optional: true });
private readonly config = inject(CONFIG_TOKEN, { self: true });
}
// 可树摇的单例
@Injectable({ providedIn: 'root' })
export class UserService {}
// ✅ v19新增:provideAppInitializer
providers: [
provideAppInitializer(() => {
const config = inject(ConfigService);
return config.load();
}),
]RxJS Interop
RxJS 互操作
typescript
import { toSignal, toObservable } from '@angular/core/rxjs-interop';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
// Observable → Signal (with custom equality in v19)
arraySignal = toSignal(this.array$, {
initialValue: [],
equal: (a, b) => a.length === b.length && a.every((v, i) => v === b[i]),
});
// Signal → Observable
count$ = toObservable(this.count);
// Auto-unsubscribe on destroy
this.data$.pipe(takeUntilDestroyed()).subscribe(data => { /* ... */ });typescript
import { toSignal, toObservable } from '@angular/core/rxjs-interop';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
// Observable → Signal(v19支持自定义相等性判断)
arraySignal = toSignal(this.array$, {
initialValue: [],
equal: (a, b) => a.length === b.length && a.every((v, i) => v === b[i]),
});
// Signal → Observable
count$ = toObservable(this.count);
// 组件销毁时自动取消订阅
this.data$.pipe(takeUntilDestroyed()).subscribe(data => { /* ... */ });Lifecycle & Rendering (v19)
生命周期与渲染(v19)
typescript
import { afterRenderEffect, afterRender, afterNextRender } from '@angular/core';
// afterRenderEffect — tracks dependencies, reruns when they change
afterRenderEffect(() => {
const el = this.chartEl().nativeElement;
this.renderChart(el, this.data());
});
// afterRender — every render cycle
afterRender(() => this.updateScrollPosition());
// afterNextRender — once after next render
afterNextRender(() => this.initializeThirdPartyLib());typescript
import { afterRenderEffect, afterRender, afterNextRender } from '@angular/core';
// afterRenderEffect — 跟踪依赖,依赖变化时重新执行
afterRenderEffect(() => {
const el = this.chartEl().nativeElement;
this.renderChart(el, this.data());
});
// afterRender — 每个渲染周期都执行
afterRender(() => this.updateScrollPosition());
// afterNextRender — 下次渲染后执行一次
afterNextRender(() => this.initializeThirdPartyLib());Pipes & Directives
管道与指令
typescript
// Pure Pipe (default)
@Pipe({ name: 'dateFormat' })
export class DateFormatPipe implements PipeTransform {
transform(timestamp: string, format: string): string {
return dateFromFormat(timestamp, 'YYYY-MM-DD HH:mm:ss').format(format);
}
}
// Attribute Directive with signal input
@Directive({ selector: '[auFocus]' })
export class FocusDirective {
private readonly elementRef = inject(ElementRef<HTMLElement>);
focused = input(true, { alias: 'auFocus', transform: booleanAttribute });
constructor() {
effect(() => {
if (this.focused()) this.elementRef.nativeElement.focus();
});
}
}typescript
// 纯管道(默认)
@Pipe({ name: 'dateFormat' })
export class DateFormatPipe implements PipeTransform {
transform(timestamp: string, format: string): string {
return dateFromFormat(timestamp, 'YYYY-MM-DD HH:mm:ss').format(format);
}
}
// 带信号输入的属性指令
@Directive({ selector: '[auFocus]' })
export class FocusDirective {
private readonly elementRef = inject(ElementRef<HTMLElement>);
focused = input(true, { alias: 'auFocus', transform: booleanAttribute });
constructor() {
effect(() => {
if (this.focused()) this.elementRef.nativeElement.focus();
});
}
}Anti-Patterns
反模式
| Avoid | Do Instead |
|---|---|
| Omit (standalone by default) |
| |
| |
| |
| Not needed in v19 |
| Manual subscription cleanup | |
| Use |
| |
| Constructor injection (verbose) | |
| |
| 应避免 | 推荐做法 |
|---|---|
| 省略(默认独立模式) |
| |
| |
| |
effect中使用 | v19中无需设置 |
| 手动清理订阅 | |
| 结合signals使用 |
| 使用ngOnInit处理异步数据 | |
| 构造函数注入(冗余繁琐) | |
| |
Related Skills
相关技能
| Skill | When to Use Together |
|---|---|
| Material components, CDK, theming |
| Styling with Tailwind CSS |
| TypeScript patterns, generics, type safety |
| When working with Aurora YAML schemas |
| 技能 | 协同使用场景 |
|---|---|
| Material组件、CDK、主题定制 |
| 使用Tailwind CSS进行样式开发 |
| TypeScript模式、泛型、类型安全 |
| 处理Aurora YAML模式时 |