下面这个示例把 Angular 的关键主线串在一起:
standalone- component
- template
- directive
- service
- dependency injection
- router
- RxJS
#1. main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { AppComponent } from './app.component';
import { routes } from './app.routes';
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes),
provideHttpClient(),
],
});#2. app.routes.ts
import { Routes } from '@angular/router';
import { DirectoryComponent } from './directory.component';
import { DepartmentPageComponent } from './department-page.component';
export const routes: Routes = [
{
path: '',
component: DirectoryComponent,
title: '园区通讯录',
},
{
path: 'departments/:id',
component: DepartmentPageComponent,
title: '部门详情',
},
];#3. dispatch.service.ts
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface Department {
id: string;
name: string;
manager: string;
online: boolean;
}
@Injectable({
providedIn: 'root',
})
export class DispatchService {
private http = inject(HttpClient);
search(keyword: string): Observable<Department[]> {
return this.http.get<Department[]>('/api/departments', {
params: { q: keyword },
});
}
getOne(id: string): Observable<Department> {
return this.http.get<Department>(`/api/departments/${id}`);
}
}#4. online-badge.directive.ts
import { Directive, ElementRef, Input, OnChanges, inject } from '@angular/core';
@Directive({
selector: '[appOnlineBadge]',
standalone: true,
})
export class OnlineBadgeDirective implements OnChanges {
private el = inject(ElementRef<HTMLElement>);
@Input('appOnlineBadge') online = false;
ngOnChanges() {
const native = this.el.nativeElement;
native.style.paddingLeft = '12px';
native.style.borderLeft = this.online ? '4px solid #10893e' : '4px solid #8a8a8a';
}
}#5. directory.component.ts
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { RouterLink } from '@angular/router';
import {
debounceTime,
distinctUntilChanged,
startWith,
switchMap,
} from 'rxjs/operators';
import { DispatchService } from './dispatch.service';
import { OnlineBadgeDirective } from './online-badge.directive';
@Component({
selector: 'campus-directory',
standalone: true,
imports: [CommonModule, ReactiveFormsModule, RouterLink, OnlineBadgeDirective],
template: `
<main class="panel">
<h1>企业中控园区通讯录</h1>
<input
[formControl]="keyword"
placeholder="搜索部门或负责人"
/>
<p>输入会经过防抖,再触发最新请求。</p>
<ul>
<li
*ngFor="let item of departments$ | async"
[appOnlineBadge]="item.online"
>
<a [routerLink]="['/departments', item.id]">{{ item.name }}</a>
<span>负责人:{{ item.manager }}</span>
<strong>{{ item.online ? '在线' : '离线' }}</strong>
</li>
</ul>
</main>
`,
})
export class DirectoryComponent {
private dispatch = inject(DispatchService);
keyword = new FormControl('', { nonNullable: true });
departments$ = this.keyword.valueChanges.pipe(
startWith(''),
debounceTime(300),
distinctUntilChanged(),
switchMap((keyword) => this.dispatch.search(keyword))
);
}#6. department-page.component.ts
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { map, distinctUntilChanged, switchMap } from 'rxjs/operators';
import { DispatchService } from './dispatch.service';
@Component({
selector: 'department-page',
standalone: true,
imports: [CommonModule, RouterLink],
template: `
<section *ngIf="department$ | async as department; else loading">
<a routerLink="/">返回通讯录</a>
<h2>{{ department.name }}</h2>
<p>负责人:{{ department.manager }}</p>
<p>状态:{{ department.online ? '在线' : '离线' }}</p>
</section>
<ng-template #loading>
<p>部门信息加载中...</p>
</ng-template>
`,
})
export class DepartmentPageComponent {
private route = inject(ActivatedRoute);
private dispatch = inject(DispatchService);
department$ = this.route.paramMap.pipe(
map((params) => params.get('id') ?? ''),
distinctUntilChanged(),
switchMap((id) => this.dispatch.getOne(id))
);
}#7. app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
template: `
<router-outlet />
`,
})
export class AppComponent {}#8. 这套代码背后的流程图
用户输入关键字
│
▼
FormControl.valueChanges
│
▼
debounceTime / distinctUntilChanged
│
▼
switchMap -> DispatchService.search()
│
▼
HttpClient 返回 Observable<Department[]>
│
▼
模板 async pipe 订阅
│
▼
目录列表渲染
点击某个部门
│
▼
Router 导航到 /departments/:id
│
▼
ActivatedRoute.paramMap
│
▼
switchMap -> DispatchService.getOne(id)
│
▼
详情组件渲染这就是 Angular 的真实工作方式:
组件负责楼,模板负责面板,路由负责道路,服务负责公共能力,DI 负责供给,RxJS 负责事件流。