angular-directives
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAngular Directives
Angular Directives
Version: Angular 21 (2025)
Tags: Directives, Components, DOM, Custom Directives
版本: Angular 21 (2025)
标签: Directives, Components, DOM, Custom Directives
API Changes
API变更
This section documents recent version-specific API changes.
-
NEW: Directive composition API — Useto compose directives source
hostDirectives -
NEW: Signal inputs in directives — Usefor reactive directive properties
input() -
NEW: Standalone directives — All directives can be standalone
-
NEW: Control flow syntax — @if, @for, @switch replace *ngIf, *ngFor
本部分记录了近期版本对应的API变动。
-
新增:指令组合API — 使用来组合指令 来源
hostDirectives -
新增:指令中的Signal输入 — 使用定义响应式指令属性
input() -
新增:独立指令 — 所有指令都可以声明为独立类型
-
新增:控制流语法 — @if、@for、@switch 替代 *ngIf、*ngFor
Best Practices
最佳实践
- Create attribute directives for reusable behavior
ts
import { Directive, ElementRef, Renderer2, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
@Input() set appHighlight(color: string) {
this.renderer.setStyle(this.el.nativeElement, 'background-color', color || 'yellow');
}
constructor(private el: ElementRef, private renderer: Renderer2) {}
}- Use @Input and @Output for directive communication
ts
@Directive({
selector: '[appClickTracker]'
})
export class ClickTrackerDirective {
@Input() trackName = 'default';
@Output() clicked = new EventEmitter<string>();
@HostListener('click')
onClick() {
this.clicked.emit(this.trackName);
}
}- Use TemplateRef for structural directives
ts
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appUnless]'
})
export class UnlessDirective {
@Input() set appUnless(condition: boolean) {
if (!condition) {
this.vcRef.createEmbeddedView(this.templateRef);
} else {
this.vcRef.clear();
}
}
constructor(
private templateRef: TemplateRef<any>,
private vcRef: ViewContainerRef
) {}
}- Use ViewContainerRef for complex structural logic
ts
@Directive({ selector: '[appDynamic]' })
export class DynamicDirective {
constructor(
private vcr: ViewContainerRef,
private templateRef: TemplateRef<any>
) {
this.vcr.createEmbeddedView(this.templateRef);
}
}- Use hostDirectives for composition
ts
@Component({
selector: 'app-card',
standalone: true,
imports: [HighlightDirective],
hostDirectives: [
{
directive: HighlightDirective,
inputs: ['appHighlight: highlight']
}
],
template: `<ng-content></ng-content>`
})
export class CardComponent {}- Use standalone: true for modern directives
ts
@Directive({
selector: '[appStandalone]',
standalone: true
})
export class StandaloneDirective {}- Use signals in directives
ts
@Directive({
selector: '[appSignal]'
})
export class SignalDirective {
value = input<string>('');
ngOnInit() {
console.log(this.value());
}
}- Use @HostBinding for property binding
ts
@Directive({
selector: '[appDisable]'
})
export class DisableDirective {
@Input() set disabled(value: boolean) {
this.hostBinding.nativeElement.disabled = value;
}
constructor(private hostBinding: ElementRef) {}
}- Keep directives focused — One responsibility
ts
// ✅ Good - focused directive
@Directive({ selector: '[appTooltip]' })
// ❌ Bad - too many responsibilities
@Directive({ selector: '[appTooltip][appTooltipMaxWidth][appTooltipTheme]' })- Use descriptive selectors
ts
// ✅ Good
@Directive({ selector: '[appUserCard]' })
// ❌ Bad
@Directive({ selector: '[appUc]' })- 为可复用行为创建属性指令
ts
import { Directive, ElementRef, Renderer2, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
@Input() set appHighlight(color: string) {
this.renderer.setStyle(this.el.nativeElement, 'background-color', color || 'yellow');
}
constructor(private el: ElementRef, private renderer: Renderer2) {}
}- 使用@Input和@Output实现指令通信
ts
@Directive({
selector: '[appClickTracker]'
})
export class ClickTrackerDirective {
@Input() trackName = 'default';
@Output() clicked = new EventEmitter<string>();
@HostListener('click')
onClick() {
this.clicked.emit(this.trackName);
}
}- 结构指令使用TemplateRef
ts
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[appUnless]'
})
export class UnlessDirective {
@Input() set appUnless(condition: boolean) {
if (!condition) {
this.vcRef.createEmbeddedView(this.templateRef);
} else {
this.vcRef.clear();
}
}
constructor(
private templateRef: TemplateRef<any>,
private vcRef: ViewContainerRef
) {}
}- 复杂结构逻辑使用ViewContainerRef
ts
@Directive({ selector: '[appDynamic]' })
export class DynamicDirective {
constructor(
private vcr: ViewContainerRef,
private templateRef: TemplateRef<any>
) {
this.vcr.createEmbeddedView(this.templateRef);
}
}- 使用hostDirectives实现指令组合
ts
@Component({
selector: 'app-card',
standalone: true,
imports: [HighlightDirective],
hostDirectives: [
{
directive: HighlightDirective,
inputs: ['appHighlight: highlight']
}
],
template: `<ng-content></ng-content>`
})
export class CardComponent {}- 现代指令配置
standalone: true
ts
@Directive({
selector: '[appStandalone]',
standalone: true
})
export class StandaloneDirective {}- 在指令中使用signals
ts
@Directive({
selector: '[appSignal]'
})
export class SignalDirective {
value = input<string>('');
ngOnInit() {
console.log(this.value());
}
}- 使用@HostBinding实现属性绑定
ts
@Directive({
selector: '[appDisable]'
})
export class DisableDirective {
@Input() set disabled(value: boolean) {
this.hostBinding.nativeElement.disabled = value;
}
constructor(private hostBinding: ElementRef) {}
}- 保持指令职责单一 — 一个指令只负责一项功能
ts
// ✅ 好 - 职责聚焦的指令
@Directive({ selector: '[appTooltip]' })
// ❌ 不好 - 职责过多
@Directive({ selector: '[appTooltip][appTooltipMaxWidth][appTooltipTheme]' })- 使用表意清晰的选择器
ts
// ✅ 好
@Directive({ selector: '[appUserCard]' })
// ❌ 不好
@Directive({ selector: '[appUc]' })