Angular 速查表
本文档汇总了 Angular 开发中常用的语法和知识点,方便快速查阅。
项目命令
# 创建项目
ng new project-name
# 创建组件
ng g c component-name
# 创建服务
ng g s service-name
# 创建指令
ng g d directive-name
# 创建管道
ng g p pipe-name
# 启动开发服务器
ng serve
# 构建项目
ng build
# 运行测试
ng test
# 代码格式化
ng lint
组件
基本组件结构
import { Component } from '@angular/core';
@Component({
selector: 'app-example',
standalone: true,
imports: [CommonModule],
template: `<p>{{ message }}</p>`,
styles: [`p { color: blue; }`]
})
export class ExampleComponent {
message = 'Hello Angular';
}
组件通信
// 输入属性
@Input() name = '';
@Input({ required: true }) id!: number;
// Signal 输入
name = input<string>();
age = input.required<number>();
// 输出属性
@Output() valueChange = new EventEmitter<string>();
// Model(双向绑定)
value = model<number>(0);
生命周期
ngOnChanges(changes: SimpleChanges) { }
ngOnInit() { }
ngDoCheck() { }
ngAfterContentInit() { }
ngAfterContentChecked() { }
ngAfterViewInit() { }
ngAfterViewChecked() { }
ngOnDestroy() { }
Signals
import { signal, computed, effect } from '@angular/core';
// 可写信号
count = signal(0);
count.set(5);
count.update(v => v + 1);
// 计算信号
double = computed(() => this.count() * 2);
// 效果
effect(() => {
console.log('Count changed:', this.count());
});
模板语法
数据绑定
<!-- 插值 -->
<p>{{ name }}</p>
<!-- 属性绑定 -->
<img [src]="imageUrl">
<div [class.active]="isActive">
<div [style.color]="textColor">
<!-- 事件绑定 -->
<button (click)="onClick()">点击</button>
<input (keyup.enter)="onEnter()">
<!-- 双向绑定 -->
<input [(ngModel)]="name">
控制流
<!-- 条件渲染 -->
@if (show) {
<p>显示内容</p>
} @else {
<p>隐藏内容</p>
}
<!-- 循环 -->
@for (item of items; track item.id; let i = $index) {
<li>{{ i }}: {{ item.name }}</li>
} @empty {
<p>列表为空</p>
}
<!-- 开关 -->
@switch (status) {
@case ('loading') { <p>加载中</p> }
@case ('success') { <p>成功</p> }
@default { <p>默认</p> }
}
模板引用
<input #nameInput>
<button (click)="nameInput.focus()">聚焦</button>
<p>{{ nameInput.value }}</p>
指令
内置属性指令
<div [ngClass]="{ active: isActive }">
<div [ngStyle]="{ 'color': textColor, 'font-size': fontSize + 'px' }">
内置结构指令
<div *ngIf="show; else hidden">显示</div>
<ng-template #hidden><p>隐藏</p></ng-template>
<li *ngFor="let item of items; let i = index; trackBy: trackById">
{{ i }}: {{ item.name }}
</li>
<div [ngSwitch]="value">
<p *ngSwitchCase="'a'">A</p>
<p *ngSwitchDefault>其他</p>
</div>
自定义指令
@Directive({
selector: '[appHighlight]',
standalone: true
})
export class HighlightDirective {
@Input() appHighlight = 'yellow';
@HostBinding('style.backgroundColor') bgColor = '';
@HostListener('mouseenter') onMouseEnter() {
this.bgColor = this.appHighlight;
}
@HostListener('mouseleave') onMouseLeave() {
this.bgColor = '';
}
}
服务与依赖注入
创建服务
import { Injectable, inject } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class UserService {
private http = inject(HttpClient);
getUsers() {
return this.http.get<User[]>('/api/users');
}
}
注入服务
// 推荐方式
userService = inject(UserService);
// 构造函数方式
constructor(private userService: UserService) {}
表单
响应式表单
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
// 使用 FormBuilder
form = this.fb.group({
name: ['', [Validators.required, Validators.minLength(3)]],
email: ['', [Validators.required, Validators.email]],
address: this.fb.group({
city: [''],
street: ['']
}),
hobbies: this.fb.array([
this.fb.control('')
])
});
// 验证状态
form.valid
form.invalid
form.get('name')?.errors
form.get('name')?.touched
form.get('name')?.dirty
模板驱动表单
<form #form="ngForm" (ngSubmit)="onSubmit(form)">
<input name="name" [(ngModel)]="user.name" required minlength="3" #name="ngModel">
<p *ngIf="name.invalid && name.touched">名称无效</p>
</form>
路由
路由配置
export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'users/:id', component: UserDetailComponent },
{ path: 'admin', canActivate: [authGuard], loadChildren: () => import('./admin/routes') },
{ path: '**', redirectTo: '' }
];
路由导航
<a routerLink="/users">用户列表</a>
<a [routerLink]="['/users', userId]">用户详情</a>
<a routerLink="/users" [queryParams]="{ page: 1 }">用户列表</a>
<a routerLink="/home" routerLinkActive="active">首页</a>
// 编程式导航
router.navigate(['/users', 123]);
router.navigate(['/users'], { queryParams: { page: 1 } });
// 获取参数
route.snapshot.paramMap.get('id');
route.snapshot.queryParamMap.get('page');
路由守卫
// CanActivate
export const authGuard: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
return auth.isLoggedIn() || inject(Router).createUrlTree(['/login']);
};
// CanDeactivate
export const canDeactivateGuard: CanDeactivateFn<CanComponentDeactivate> = (component) => {
return component.canDeactivate();
};
HTTP
基本请求
// GET
http.get<User[]>('/api/users');
http.get<User>('/api/users/1');
// POST
http.post<User>('/api/users', { name: '张三' });
// PUT
http.put<User>('/api/users/1', { name: '李四' });
// DELETE
http.delete<void>('/api/users/1');
拦截器
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const auth = inject(AuthService);
const authReq = req.clone({
setHeaders: { Authorization: `Bearer ${auth.getToken()}` }
});
return next(authReq);
};
管道
内置管道
<!-- 日期 -->
{{ date | date:'yyyy-MM-dd' }}
<!-- 大小写 -->
{{ text | uppercase }}
{{ text | lowercase }}
{{ text | titlecase }}
<!-- 数字 -->
{{ num | number:'1.0-2' }}
{{ price | currency:'CNY' }}
{{ ratio | percent }}
<!-- 其他 -->
{{ obj | json }}
{{ arr | slice:0:5 }}
{{ data$ | async }}
自定义管道
@Pipe({
name: 'truncate',
standalone: true
})
export class TruncatePipe implements PipeTransform {
transform(value: string, limit = 20): string {
return value.length > limit ? value.substring(0, limit) + '...' : value;
}
}
常用 RxJS 操作符
import { of, from, interval, merge, combineLatest } from 'rxjs';
import { map, filter, tap, switchMap, catchError, debounceTime, distinctUntilChanged } from 'rxjs/operators';
// 创建 Observable
of(1, 2, 3);
from([1, 2, 3]);
interval(1000);
// 转换
pipe(
map(x => x * 2),
filter(x => x > 5),
tap(x => console.log(x))
);
// 组合
pipe(
debounceTime(300),
distinctUntilChanged(),
switchMap(term => http.get(`/search?q=${term}`))
);
// 错误处理
pipe(
catchError(error => of([]))
);
测试
组件测试
describe('MyComponent', () => {
let component: MyComponent;
let fixture: ComponentFixture<MyComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MyComponent]
}).compileComponents();
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
服务测试
describe('MyService', () => {
let service: MyService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [MyService]
});
service = TestBed.inject(MyService);
httpMock = TestBed.inject(HttpTestingController);
});
it('should fetch data', () => {
service.getData().subscribe(data => {
expect(data).toEqual([]);
});
httpMock.expectOne('/api/data').flush([]);
});
});
常见问题
组件不更新
- 检查是否调用了
fixture.detectChanges() - 检查 Signal 是否正确使用
()读取 - 检查是否在 Angular 区域外修改了数据
路由不工作
- 检查
RouterOutlet是否已导入 - 检查路由配置是否正确
- 检查
provideRouter是否已添加
表单验证不生效
- 检查是否导入了
ReactiveFormsModule或FormsModule - 检查验证器是否正确配置
- 检查是否触发了
touched或dirty状态
HTTP 请求失败
- 检查是否配置了
provideHttpClient() - 检查 CORS 配置
- 检查拦截器是否正确配置