angular-19

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

When 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
standalone: true
needed.
typescript
// ✅ 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: true
typescript
// ✅ 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

反模式

AvoidDo Instead
standalone: true
(redundant in v19)
Omit (standalone by default)
@Input()
decorator
input()
/
input.required()
@Output()
decorator
output()
@ViewChild()
decorator
viewChild()
/
viewChild.required()
allowSignalWrites
in effect
Not needed in v19
Manual subscription cleanup
takeUntilDestroyed()
ChangeDetectionStrategy.Default
Use
OnPush
with signals
ngOnInit
for async data
resource()
/
rxResource()
Constructor injection (verbose)
inject()
function
APP_INITIALIZER
token
provideAppInitializer()

应避免推荐做法
standalone: true
(v19中冗余)
省略(默认独立模式)
@Input()
装饰器
input()
/
input.required()
@Output()
装饰器
output()
@ViewChild()
装饰器
viewChild()
/
viewChild.required()
effect中使用
allowSignalWrites
v19中无需设置
手动清理订阅
takeUntilDestroyed()
ChangeDetectionStrategy.Default
结合signals使用
OnPush
使用ngOnInit处理异步数据
resource()
/
rxResource()
构造函数注入(冗余繁琐)
inject()
函数
APP_INITIALIZER
令牌
provideAppInitializer()

Related Skills

相关技能

SkillWhen to Use Together
angular-material
Material components, CDK, theming
tailwind
Styling with Tailwind CSS
typescript
TypeScript patterns, generics, type safety
aurora-schema
When working with Aurora YAML schemas

技能协同使用场景
angular-material
Material组件、CDK、主题定制
tailwind
使用Tailwind CSS进行样式开发
typescript
TypeScript模式、泛型、类型安全
aurora-schema
处理Aurora YAML模式时

Resources

参考资源