angular-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAngular 21 Best Practices
Angular 21 最佳实践
TypeScript
TypeScript
- Use strict type checking
- Prefer type inference when type is obvious
- Avoid ; use
anywhen type is uncertainunknown
- 使用严格类型检查
- 当类型明确时优先使用类型推断
- 避免使用;当类型不确定时使用
anyunknown
Components
组件
- Always use standalone components (do NOT set — it's the default in v20+)
standalone: true - Set
changeDetection: ChangeDetectionStrategy.OnPush - Use and
input()functions instead of decoratorsoutput() - Use for derived state
computed() - Keep components small and single-responsibility
- Prefer inline templates for small components
- Use Reactive forms over Template-driven
- Use bindings instead of
classngClass - Use bindings instead of
stylengStyle - For external templates/styles, use paths relative to the component TS file
- Do NOT use /
@HostBinding— use the@HostListenerobject in the decorator insteadhost
- 始终使用独立组件(无需设置——在v20+版本中这是默认值)
standalone: true - 设置
changeDetection: ChangeDetectionStrategy.OnPush - 使用和
input()函数而非装饰器output() - 使用处理派生状态
computed() - 保持组件小巧且单一职责
- 小型组件优先使用内联模板
- 优先使用响应式表单而非模板驱动表单
- 使用绑定替代
classngClass - 使用绑定替代
stylengStyle - 对于外部模板/样式,使用相对于组件TS文件的路径
- 不要使用/
@HostBinding——改用装饰器中的@HostListener对象host
State Management with Signals
基于Signals的状态管理
- Use signals for local component state
- Use for derived state
computed() - Keep state transformations pure and predictable
- Do NOT use on signals — use
mutateorupdateinsteadset
For complex derived state patterns, see references/signal-patterns.md.
- 使用signals管理组件本地状态
- 使用处理派生状态
computed() - 保持状态转换纯净且可预测
- 不要对signals使用——改用
mutate或updateset
如需了解复杂的派生状态模式,请查看references/signal-patterns.md。
Resources (Async Data)
资源(异步数据)
Use for async data fetching with signals:
resource()typescript
const userResource = resource({
params: () => ({ id: userId() }),
loader: ({ params, abortSignal }) => fetch(`/api/users/${params.id}`, { signal: abortSignal }),
});
const userName = computed(() => userResource.hasValue() ? userResource.value().name : undefined);Key patterns:
resource- returns
params→ loader doesn't run, status becomesundefined'idle' - Use to cancel in-flight requests
abortSignal - Check before accessing
hasValue()to handle loading/error statesvalue() - Status values: ,
'idle','loading','reloading','resolved','error''local'
结合signals使用进行异步数据获取:
resource()typescript
const userResource = resource({
params: () => ({ id: userId() }),
loader: ({ params, abortSignal }) => fetch(`/api/users/${params.id}`, { signal: abortSignal }),
});
const userName = computed(() => userResource.hasValue() ? userResource.value().name : undefined);resource- 返回
params→ 加载器不会运行,状态变为undefined'idle' - 使用取消进行中的请求
abortSignal - 在访问前检查
value()以处理加载/错误状态hasValue() - 状态值包括:,
'idle','loading','reloading','resolved','error''local'
Templates
模板
- Use native control flow: ,
@if,@for(NOT@switch,*ngIf,*ngFor)*ngSwitch - Use async pipe for observables
- Keep templates simple — no complex logic
- Do NOT use arrow functions in templates (not supported)
- Do NOT assume globals like are available
new Date()
- 使用原生控制流:,
@if,@for(不要使用@switch,*ngIf,*ngFor)*ngSwitch - 对可观察对象使用async管道
- 保持模板简洁——避免复杂逻辑
- 不要在模板中使用箭头函数(不支持)
- 不要假设等全局对象可用
new Date()
Services
服务
- Single responsibility per service
- Use for singletons
providedIn: 'root' - Use function instead of constructor injection
inject()
typescript
@Injectable({ providedIn: 'root' })
export class UserService {
private readonly http = inject(HttpClient);
}- 每个服务单一职责
- 单例服务使用
providedIn: 'root' - 使用函数而非构造函数注入
inject()
typescript
@Injectable({ providedIn: 'root' })
export class UserService {
private readonly http = inject(HttpClient);
}Routing
路由
- Implement lazy loading for feature routes:
typescript
export const routes: Routes = [
{
path: 'admin',
loadComponent: () => import('./admin/admin.page').then(m => m.AdminPage),
},
];- 为功能路由实现懒加载:
typescript
export const routes: Routes = [
{
path: 'admin',
loadComponent: () => import('./admin/admin.page').then(m => m.AdminPage),
},
];File Naming
文件命名
- Routable view components: ,
file-name.page.ts,file-name.page.htmlfile-name.page.css - Regular components:
file-name.component.ts - Services:
file-name.service.ts
- 可路由视图组件:,
file-name.page.ts,file-name.page.htmlfile-name.page.css - 常规组件:
file-name.component.ts - 服务:
file-name.service.ts
Icons (ng-icon)
图标(ng-icon)
typescript
import { NgIcon, provideIcons } from '@ng-icons/core';
import { heroSparkles, heroTrash } from '@ng-icons/heroicons/outline';
@Component({
selector: 'app-example',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgIcon],
providers: [provideIcons({ heroSparkles, heroTrash })],
template: `<ng-icon name="heroSparkles" />`,
})
export class ExampleComponent {}typescript
import { NgIcon, provideIcons } from '@ng-icons/core';
import { heroSparkles, heroTrash } from '@ng-icons/heroicons/outline';
@Component({
selector: 'app-example',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgIcon],
providers: [provideIcons({ heroSparkles, heroTrash })],
template: `<ng-icon name="heroSparkles" />`,
})
export class ExampleComponent {}Images
图片
- Use for all static images
NgOptimizedImage - does NOT work for inline base64 images
NgOptimizedImage
- 所有静态图片使用
NgOptimizedImage - 不适用于内联base64图片
NgOptimizedImage
Styling
样式
- Use Tailwind 4.1 for CSS (see tailwind skill if available)
- Angular CDK is available when needed
- 使用Tailwind 4.1编写CSS(若有相关技能可查看Tailwind技能文档)
- 必要时可使用Angular CDK
Accessibility
可访问性
- MUST pass all AXE checks
- MUST meet WCAG AA minimums: focus management, color contrast, ARIA attributes
- 必须通过所有AXE检查
- 必须满足WCAG AA最低标准:焦点管理、颜色对比度、ARIA属性