【Angular主要内容归纳】

@Angular主要内容归纳

Angular 完整教程 - 核心内容详解

目录

  1. 组件 (Components)
  2. 模块 (Modules)
  3. 服务 (Services)
  4. 依赖注入 (Dependency Injection)
  5. 路由 (Routing)
  6. 数据绑定 (Data Binding)
  7. 指令 (Directives)
  8. 管道 (Pipes)
  9. HTTP 客户端
  10. 表单 (Forms)
  11. 生命周期钩子 (Lifecycle Hooks)
  12. RxJS 和 Observable
  13. 状态管理
  14. 测试

1. 组件 (Components)

组件是 Angular 应用的基本构建块,每个组件包含:

  • 模板 (Template): HTML 视图
  • 类 (Class): 组件逻辑和属性
  • 样式 (Styles): CSS/SCSS 样式
  • 元数据 (Metadata): @Component 装饰器

示例:基础组件

// app.component.tsimport{ Component }from'@angular/core';@Component({ selector:'app-root', templateUrl:'./app.component.html', styleUrls:['./app.component.css']})exportclassAppComponent{ title ='我的 Angular 应用'; count =0;increment(){this.count++;}decrement(){this.count--;}}
<!-- app.component.html --><divclass="container"><h1>{{ title }}</h1><p>计数器: {{ count }}</p><button(click)="increment()">增加</button><button(click)="decrement()">减少</button></div>

示例:带输入输出的组件

// user-card.component.tsimport{ Component, Input, Output, EventEmitter }from'@angular/core';@Component({ selector:'app-user-card', template:` <div> <h3>{{ user.name }}</h3> <p>邮箱: {{ user.email }}</p> <button (click)="onSelect()">选择用户</button> </div> `})exportclassUserCardComponent{@Input() user!:{ name:string; email:string};@Output() userSelected =newEventEmitter<string>();onSelect(){this.userSelected.emit(this.user.email);}}
// parent.component.ts@Component({ selector:'app-parent', template:` <app-user-card [user]="currentUser" (userSelected)="handleUserSelection($event)"> </app-user-card> `})exportclassParentComponent{ currentUser ={ name:'张三', email:'[email protected]'};handleUserSelection(email:string){console.log('选中的用户邮箱:', email);}}

2. 模块 (Modules)

模块用于组织应用代码,NgModule 是 Angular 的模块系统。

示例:根模块

// app.module.tsimport{ NgModule }from'@angular/core';import{ BrowserModule }from'@angular/platform-browser';import{ FormsModule }from'@angular/forms';import{ HttpClientModule }from'@angular/common/http';import{ AppComponent }from'./app.component';import{ UserCardComponent }from'./user-card.component';import{ UserService }from'./user.service';@NgModule({ declarations:[ AppComponent, UserCardComponent ], imports:[ BrowserModule, FormsModule, HttpClientModule ], providers:[ UserService ], bootstrap:[AppComponent]})exportclassAppModule{}

示例:功能模块

// user.module.tsimport{ NgModule }from'@angular/core';import{ CommonModule }from'@angular/common';import{ UserListComponent }from'./user-list.component';import{ UserDetailComponent }from'./user-detail.component';import{ UserService }from'./user.service';@NgModule({ declarations:[ UserListComponent, UserDetailComponent ], imports:[ CommonModule ], providers:[ UserService ], exports:[ UserListComponent ]})exportclassUserModule{}

3. 服务 (Services)

服务用于封装业务逻辑、数据访问和共享功能。

示例:基础服务

// user.service.tsimport{ Injectable }from'@angular/core';import{ HttpClient }from'@angular/common/http';import{ Observable }from'rxjs';exportinterfaceUser{ id:number; name:string; email:string;}@Injectable({ providedIn:'root'// 根级别注入,单例模式})exportclassUserService{private apiUrl ='https://api.example.com/users';constructor(private http: HttpClient){}getUsers(): Observable<User[]>{returnthis.http.get<User[]>(this.apiUrl);}getUserById(id:number): Observable<User>{returnthis.http.get<User>(`${this.apiUrl}/${id}`);}createUser(user: User): Observable<User>{returnthis.http.post<User>(this.apiUrl, user);}updateUser(id:number, user: Partial<User>): Observable<User>{returnthis.http.put<User>(`${this.apiUrl}/${id}`, user);}deleteUser(id:number): Observable<void>{returnthis.http.delete<void>(`${this.apiUrl}/${id}`);}}

示例:带缓存的服务

// product.service.tsimport{ Injectable }from'@angular/core';import{ BehaviorSubject, Observable }from'rxjs';import{ tap }from'rxjs/operators';@Injectable({ providedIn:'root'})exportclassProductService{private productsSubject =newBehaviorSubject<any[]>([]);public products$ =this.productsSubject.asObservable();constructor(private http: HttpClient){this.loadProducts();}privateloadProducts(){this.http.get<any[]>('/api/products').pipe(tap(products =>this.productsSubject.next(products))).subscribe();}getProducts(): Observable<any[]>{returnthis.products$;}addProduct(product:any){const current =this.productsSubject.value;this.productsSubject.next([...current, product]);}}

4. 依赖注入 (Dependency Injection)

Angular 的依赖注入系统自动管理依赖关系。

示例:构造函数注入

// component.tsimport{ Component }from'@angular/core';import{ UserService }from'./user.service';@Component({ selector:'app-user-list', template:'<div>用户列表</div>'})exportclassUserListComponent{constructor(private userService: UserService){// Angular 自动注入 UserService}ngOnInit(){this.userService.getUsers().subscribe(users =>{console.log(users);});}}

示例:可选依赖和注入令牌

// logger.service.tsimport{ Injectable, InjectionToken, Optional, Inject }from'@angular/core';exportconstLOG_LEVEL=newInjectionToken<string>('LOG_LEVEL');@Injectable({ providedIn:'root'})exportclassLoggerService{constructor(@Optional()@Inject(LOG_LEVEL)private logLevel:string='INFO'){console.log('日志级别:',this.logLevel);}log(message:string){console.log(`[${this.logLevel}] ${message}`);}}
// app.module.ts providers:[{ provide:LOG_LEVEL, useValue:'DEBUG'}]

5. 路由 (Routing)

Angular Router 用于实现单页应用的路由导航。

示例:基础路由配置

// app-routing.module.tsimport{ NgModule }from'@angular/core';import{ RouterModule, Routes }from'@angular/router';import{ HomeComponent }from'./home/home.component';import{ UserListComponent }from'./user/user-list.component';import{ UserDetailComponent }from'./user/user-detail.component';import{ NotFoundComponent }from'./not-found/not-found.component';const routes: Routes =[{ path:'', redirectTo:'/home', pathMatch:'full'},{ path:'home', component: HomeComponent },{ path:'users', component: UserListComponent },{ path:'users/:id', component: UserDetailComponent },{ path:'**', component: NotFoundComponent }];@NgModule({ imports:[RouterModule.forRoot(routes)], exports:[RouterModule]})exportclassAppRoutingModule{}

示例:路由守卫

// auth.guard.tsimport{ Injectable }from'@angular/core';import{ CanActivate, Router }from'@angular/router';import{ AuthService }from'./auth.service';@Injectable({ providedIn:'root'})exportclassAuthGuardimplementsCanActivate{constructor(private authService: AuthService,private router: Router ){}canActivate():boolean{if(this.authService.isAuthenticated()){returntrue;}else{this.router.navigate(['/login']);returnfalse;}}}
// 在路由中使用守卫const routes: Routes =[{ path:'dashboard', component: DashboardComponent, canActivate:[AuthGuard]}];

示例:路由参数和查询参数

// user-detail.component.tsimport{ Component, OnInit }from'@angular/core';import{ ActivatedRoute, Router }from'@angular/router';@Component({ selector:'app-user-detail', template:` <div> <h2>用户详情</h2> <p>ID: {{ userId }}</p> <p>模式: {{ mode }}</p> <button (click)="goBack()">返回</button> </div> `})exportclassUserDetailComponentimplementsOnInit{ userId!:number; mode!:string;constructor(private route: ActivatedRoute,private router: Router ){}ngOnInit(){// 获取路由参数this.userId =+this.route.snapshot.paramMap.get('id')!;// 获取查询参数this.mode =this.route.snapshot.queryParamMap.get('mode')||'view';// 监听参数变化this.route.paramMap.subscribe(params =>{this.userId =+params.get('id')!;});}goBack(){this.router.navigate(['/users']);}}

6. 数据绑定 (Data Binding)

Angular 支持多种数据绑定方式。

示例:插值绑定

<!-- 显示组件属性 --><h1>{{ title }}</h1><p>当前时间: {{ getCurrentTime() }}</p>

示例:属性绑定

<!-- 绑定 HTML 属性 --><img[src]="imageUrl"[alt]="imageAlt"><!-- 绑定 DOM 属性 --><button[disabled]="isDisabled">提交</button><!-- 绑定类 --><div[class.active]="isActive"[class.error]="hasError"></div><!-- 绑定样式 --><div[style.color]="textColor"[style.font-size.px]="fontSize"></div>

示例:事件绑定

<!-- 点击事件 --><button(click)="handleClick()">点击</button><!-- 带事件对象 --><button(click)="handleClick($event)">点击</button><!-- 输入事件 --><input(input)="onInput($event)"(keyup.enter)="onEnter()">

示例:双向绑定

// component.tsexportclassFormComponent{ username =''; email ='';}
<!-- 使用 ngModel 双向绑定 --><input[(ngModel)]="username"placeholder="用户名"><input[(ngModel)]="email"type="email"placeholder="邮箱"><!-- 等价写法 --><input[ngModel]="username"(ngModelChange)="username = $event">

7. 指令 (Directives)

指令用于扩展 HTML 元素的功能。

示例:结构型指令

<!-- *ngIf - 条件渲染 --><div*ngIf="isLoggedIn">欢迎回来!</div><div*ngIf="!isLoggedIn">请登录</div><!-- *ngFor - 列表渲染 --><ul><li*ngFor="let user of users; let i = index; let first = first"> {{ i + 1 }}. {{ user.name }} <span*ngIf="first">(第一个)</span></li></ul><!-- *ngSwitch - 多条件判断 --><div[ngSwitch]="status"><p*ngSwitchCase="'loading'">加载中...</p><p*ngSwitchCase="'success'">成功!</p><p*ngSwitchCase="'error'">错误!</p><p*ngSwitchDefault>未知状态</p></div>

示例:属性型指令

// highlight.directive.tsimport{ Directive, ElementRef, Input, Renderer2, OnInit }from'@angular/core';@Directive({ selector:'[appHighlight]'})exportclassHighlightDirectiveimplementsOnInit{@Input() appHighlight ='yellow';@Input() defaultColor ='transparent';constructor(private el: ElementRef,private renderer: Renderer2 ){}ngOnInit(){this.setBackgroundColor(this.appHighlight ||this.defaultColor);}privatesetBackgroundColor(color:string){this.renderer.setStyle(this.el.nativeElement,'background-color', color);}}
<!-- 使用自定义指令 --><pappHighlight="yellow">高亮文本</p><p[appHighlight]="highlightColor">动态高亮</p>

示例:结构型指令(自定义)

// unless.directive.tsimport{ Directive, Input, TemplateRef, ViewContainerRef }from'@angular/core';@Directive({ selector:'[appUnless]'})exportclassUnlessDirective{private hasView =false;constructor(private templateRef: TemplateRef<any>,private viewContainer: ViewContainerRef ){}@Input()setappUnless(condition:boolean){if(!condition &&!this.hasView){this.viewContainer.createEmbeddedView(this.templateRef);this.hasView =true;}elseif(condition &&this.hasView){this.viewContainer.clear();this.hasView =false;}}}
<!-- 使用自定义结构型指令 --><div*appUnless="isHidden">这段内容在 isHidden 为 false 时显示</div>

8. 管道 (Pipes)

管道用于转换数据显示格式。

示例:内置管道

<!-- 日期管道 --><p>{{ today | date:'yyyy-MM-dd' }}</p><p>{{ today | date:'full' }}</p><!-- 货币管道 --><p>{{ price | currency:'CNY':'symbol':'1.2-2' }}</p><!-- 数字管道 --><p>{{ number | number:'1.2-2' }}</p><!-- 百分比管道 --><p>{{ ratio | percent:'1.2-2' }}</p><!-- 大写/小写管道 --><p>{{ text | uppercase }}</p><p>{{ text | lowercase }}</p><!-- 切片管道 --><p>{{ longText | slice:0:100 }}...</p><!-- JSON 管道 --><pre>{{ object | json }}</pre>

示例:自定义管道

// truncate.pipe.tsimport{ Pipe, PipeTransform }from'@angular/core';@Pipe({ name:'truncate'})exportclassTruncatePipeimplementsPipeTransform{transform(value:string, limit:number=20, trail:string='...'):string{if(!value)return'';return value.length > limit ? value.substring(0, limit)+ trail : value;}}
// filter.pipe.tsimport{ Pipe, PipeTransform }from'@angular/core';@Pipe({ name:'filter', pure:false// 非纯管道,每次变更检测都会执行})exportclassFilterPipeimplementsPipeTransform{transform(items:any[], searchText:string):any[]{if(!items)return[];if(!searchText)return items; searchText = searchText.toLowerCase();return items.filter(item => item.name.toLowerCase().includes(searchText));}}
<!-- 使用自定义管道 --><p>{{ longText | truncate:50 }}</p><div*ngFor="let item of items | filter:searchText"> {{ item.name }} </div>

9. HTTP 客户端

Angular HttpClient 用于发送 HTTP 请求。

示例:GET 请求

// user.service.tsimport{ HttpClient, HttpParams }from'@angular/common/http';import{ Observable }from'rxjs';@Injectable({ providedIn:'root'})exportclassUserService{private apiUrl ='https://api.example.com/users';constructor(private http: HttpClient){}// 简单 GET 请求getUsers(): Observable<User[]>{returnthis.http.get<User[]>(this.apiUrl);}// 带查询参数的 GET 请求searchUsers(keyword:string, page:number=1): Observable<User[]>{const params =newHttpParams().set('keyword', keyword).set('page', page.toString());returnthis.http.get<User[]>(this.apiUrl,{ params });}// 带响应类型的 GET 请求downloadFile(): Observable<Blob>{returnthis.http.get(this.apiUrl +'/export',{ responseType:'blob'});}}

示例:POST/PUT/DELETE 请求

// user.service.tscreateUser(user: User): Observable<User>{returnthis.http.post<User>(this.apiUrl, user);}updateUser(id:number, user: Partial<User>): Observable<User>{returnthis.http.put<User>(`${this.apiUrl}/${id}`, user);}deleteUser(id:number): Observable<void>{returnthis.http.delete<void>(`${this.apiUrl}/${id}`);}

示例:HTTP 拦截器

// auth.interceptor.tsimport{ Injectable }from'@angular/core';import{ HttpRequest, HttpHandler, HttpEvent, HttpInterceptor }from'@angular/common/http';import{ Observable }from'rxjs';@Injectable()exportclassAuthInterceptorimplementsHttpInterceptor{intercept( request: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>>{// 添加认证 tokenconst token = localStorage.getItem('token');if(token){ request = request.clone({ setHeaders:{ Authorization:`Bearer ${token}`}});}return next.handle(request);}}
// app.module.tsimport{HTTP_INTERCEPTORS}from'@angular/common/http'; providers:[{ provide:HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi:true}]

示例:错误处理

// user.service.tsimport{ catchError, retry }from'rxjs/operators';import{ throwError }from'rxjs';getUsers(): Observable<User[]>{returnthis.http.get<User[]>(this.apiUrl).pipe(retry(3),// 重试 3 次catchError(this.handleError));}privatehandleError(error:any){let errorMessage ='发生未知错误';if(error.error instanceofErrorEvent){ errorMessage =`错误: ${error.error.message}`;}else{ errorMessage =`错误代码: ${error.status}\n消息: ${error.message}`;}console.error(errorMessage);returnthrowError(()=> errorMessage);}

10. 表单 (Forms)

Angular 提供两种表单方式:模板驱动表单和响应式表单。

示例:模板驱动表单

// login.component.tsimport{ Component }from'@angular/core';@Component({ selector:'app-login', template:` <form #loginForm="ngForm" (ngSubmit)="onSubmit(loginForm)"> <div> <label>用户名:</label> <input name="username" ngModel required minlength="3" #username="ngModel"> <div *ngIf="username.invalid && username.touched"> <span *ngIf="username.errors?.['required']">用户名必填</span> <span *ngIf="username.errors?.['minlength']">至少3个字符</span> </div> </div> <div> <label>密码:</label> <input type="password" name="password" ngModel required> </div> <button type="submit" [disabled]="loginForm.invalid"> 登录 </button> </form> `})exportclassLoginComponent{onSubmit(form:any){if(form.valid){console.log('表单数据:', form.value);}}}

示例:响应式表单

// register.component.tsimport{ Component, OnInit }from'@angular/core';import{ FormBuilder, FormGroup, Validators, AbstractControl }from'@angular/forms';@Component({ selector:'app-register', template:` <form [formGroup]="registerForm" (ngSubmit)="onSubmit()"> <div> <label>邮箱:</label> <input formControlName="email"> <div *ngIf="email.invalid && email.touched"> <span *ngIf="email.errors?.['required']">邮箱必填</span> <span *ngIf="email.errors?.['email']">邮箱格式不正确</span> </div> </div> <div> <label>密码:</label> <input type="password" formControlName="password"> </div> <div> <label>确认密码:</label> <input type="password" formControlName="confirmPassword"> <div *ngIf="registerForm.errors?.['passwordMismatch']"> 密码不匹配 </div> </div> <button type="submit" [disabled]="registerForm.invalid"> 注册 </button> </form> `})exportclassRegisterComponentimplementsOnInit{ registerForm!: FormGroup;constructor(private fb: FormBuilder){}ngOnInit(){this.registerForm =this.fb.group({ email:['',[Validators.required, Validators.email]], password:['',[Validators.required, Validators.minLength(6)]], confirmPassword:['']},{ validators:this.passwordMatchValidator });}getemail(){returnthis.registerForm.get('email')!;}getpassword(){returnthis.registerForm.get('password')!;}getconfirmPassword(){returnthis.registerForm.get('confirmPassword')!;}passwordMatchValidator(control: AbstractControl){const password = control.get('password');const confirmPassword = control.get('confirmPassword');if(password && confirmPassword && password.value !== confirmPassword.value){return{ passwordMismatch:true};}returnnull;}onSubmit(){if(this.registerForm.valid){console.log('表单数据:',this.registerForm.value);}}}

示例:动态表单

// dynamic-form.component.tsimport{ Component }from'@angular/core';import{ FormArray, FormBuilder, FormGroup, Validators }from'@angular/forms';@Component({ selector:'app-dynamic-form', template:` <form [formGroup]="form"> <div formArrayName="hobbies"> <div *ngFor="let hobby of hobbies.controls; let i = index" [formGroupName]="i"> <input formControlName="name" placeholder="爱好名称"> <button type="button" (click)="removeHobby(i)">删除</button> </div> </div> <button type="button" (click)="addHobby()">添加爱好</button> </form> `})exportclassDynamicFormComponent{ form!: FormGroup;constructor(private fb: FormBuilder){this.form =this.fb.group({ hobbies:this.fb.array([])});}gethobbies(){returnthis.form.get('hobbies')as FormArray;}addHobby(){const hobbyGroup =this.fb.group({ name:['', Validators.required]});this.hobbies.push(hobbyGroup);}removeHobby(index:number){this.hobbies.removeAt(index);}}

11. 生命周期钩子 (Lifecycle Hooks)

组件生命周期钩子允许在组件生命周期的特定时刻执行代码。

Angular 组件生命周期(常用顺序与作用):

  1. constructor
    注入依赖,尽量不做复杂逻辑,不访问输入属性。
  2. ngOnChanges(changes: SimpleChanges)
    输入属性 @Input 变更时触发;首次也会触发。可根据 changes 做差异化处理。
  3. ngOnInit
    初始化时机,适合发起首次请求、初始化数据;此时可安全读取 @Input。
  4. ngDoCheck
    自定义变更检测钩子,谨慎使用,避免重计算导致性能问题。
  5. ngAfterContentInit / ngAfterContentChecked
    针对内容投影()的初始化/变更检查。
  6. ngAfterViewInit / ngAfterViewChecked
    视图(模板、子组件)初始化/变更后触发;此时可安全访问 @ViewChild/@ViewChildren。
  7. ngOnDestroy
    销毁前清理:取消订阅、清除计时器/监听、销毁资源。
    常用实践:
    数据初始化:放 ngOnInit。
    监听输入变化:用 ngOnChanges 或 @Input() set …。
    访问子组件/DOM:放 ngAfterViewInit。
    取消订阅:在 ngOnDestroy 里统一清理(如 takeUntil/Subscription.unsubscribe)。

示例:完整生命周期

// lifecycle.component.tsimport{ Component, OnInit, OnChanges, OnDestroy, AfterViewInit, AfterViewChecked, AfterContentInit, AfterContentChecked, DoCheck, Input, SimpleChanges }from'@angular/core';@Component({ selector:'app-lifecycle', template:'<div>{{ message }}</div>'})exportclassLifecycleComponentimplementsOnInit, OnChanges, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy {@Input() message ='';constructor(){console.log('1. constructor');}ngOnChanges(changes: SimpleChanges){console.log('2. ngOnChanges', changes);}ngOnInit(){console.log('3. ngOnInit');}ngDoCheck(){console.log('4. ngDoCheck');}ngAfterContentInit(){console.log('5. ngAfterContentInit');}ngAfterContentChecked(){console.log('6. ngAfterContentChecked');}ngAfterViewInit(){console.log('7. ngAfterViewInit');}ngAfterViewChecked(){console.log('8. ngAfterViewChecked');}ngOnDestroy(){console.log('9. ngOnDestroy - 清理资源');}}

示例:使用 ViewChild 和 AfterViewInit

// component.tsimport{ Component, ViewChild, AfterViewInit, ElementRef }from'@angular/core';@Component({ selector:'app-example', template:` <div #myDiv>内容</div> <app-child #child></app-child> `})exportclassExampleComponentimplementsAfterViewInit{@ViewChild('myDiv') myDiv!: ElementRef;@ViewChild('child') childComponent!: ChildComponent;ngAfterViewInit(){// 此时可以安全访问子组件和 DOM 元素console.log(this.myDiv.nativeElement.textContent);this.childComponent.doSomething();}}

12. RxJS 和 Observable

RxJS 是 Angular 中处理异步操作的核心库。

示例:基础 Observable

import{ Observable,of, from }from'rxjs';// 创建简单的 Observableconst simple$ =of(1,2,3); simple$.subscribe(value =>console.log(value));// 从数组创建const array$ =from([1,2,3]); array$.subscribe(value =>console.log(value));// 从 Promise 创建const promise$ =from(fetch('/api/data')); promise$.subscribe(response =>console.log(response));

示例:常用操作符

import{ map, filter, tap, catchError, switchMap, debounceTime }from'rxjs/operators';// map - 转换数据this.http.get<User[]>('/api/users').pipe(map(users => users.map(user => user.name))).subscribe(names =>console.log(names));// filter - 过滤数据this.http.get<User[]>('/api/users').pipe(filter(users => users.length >0)).subscribe(users =>console.log(users));// tap - 执行副作用操作this.http.get<User[]>('/api/users').pipe(tap(users =>console.log('获取到用户:', users))).subscribe();// switchMap - 切换 Observablethis.searchControl.valueChanges.pipe(debounceTime(300),switchMap(keyword =>this.searchUsers(keyword))).subscribe(results =>console.log(results));// catchError - 错误处理this.http.get<User[]>('/api/users').pipe(catchError(error =>{console.error('错误:', error);returnof([]);// 返回默认值})).subscribe();

示例:Subject 和 BehaviorSubject

import{ Subject, BehaviorSubject }from'rxjs';// Subject - 多播 ObservableexportclassMessageService{private messageSubject =newSubject<string>();public messages$ =this.messageSubject.asObservable();sendMessage(message:string){this.messageSubject.next(message);}}// BehaviorSubject - 带初始值的 SubjectexportclassStateService{private stateSubject =newBehaviorSubject<number>(0);public state$ =this.stateSubject.asObservable();updateState(value:number){this.stateSubject.next(value);}getCurrentState():number{returnthis.stateSubject.value;}}

示例:取消订阅

import{ Component, OnDestroy }from'@angular/core';import{ Subscription }from'rxjs';@Component({ selector:'app-example', template:'<div>{{ data }}</div>'})exportclassExampleComponentimplementsOnDestroy{ data:any;private subscription =newSubscription();ngOnInit(){// 方式1: 使用 Subscriptionconst sub1 =this.service.getData().subscribe(data =>{this.data = data;});this.subscription.add(sub1);// 方式2: 使用 takeUntilthis.service.getData().pipe(takeUntil(this.destroy$)).subscribe(data =>{this.data = data;});}ngOnDestroy(){this.subscription.unsubscribe();}}

13. 状态管理

示例:使用 Service 进行状态管理

// cart.service.tsimport{ Injectable }from'@angular/core';import{ BehaviorSubject, Observable }from'rxjs';exportinterfaceCartItem{ id:number; name:string; price:number; quantity:number;}@Injectable({ providedIn:'root'})exportclassCartService{private cartSubject =newBehaviorSubject<CartItem[]>([]);public cart$ =this.cartSubject.asObservable();getCart(): Observable<CartItem[]>{returnthis.cart$;}addItem(item: CartItem){const current =this.cartSubject.value;const existing = current.find(i => i.id === item.id);if(existing){ existing.quantity += item.quantity;}else{ current.push(item);}this.cartSubject.next([...current]);}removeItem(id:number){const current =this.cartSubject.value.filter(item => item.id !== id);this.cartSubject.next(current);}clearCart(){this.cartSubject.next([]);}getTotal():number{returnthis.cartSubject.value.reduce((sum, item)=> sum + item.price * item.quantity,0);}}

示例:使用 NgRx(状态管理库)

// cart.actions.tsimport{ createAction, props }from'@ngrx/store';import{ CartItem }from'./cart.model';exportconst addItem =createAction('[Cart] Add Item',props<{ item: CartItem }>());exportconst removeItem =createAction('[Cart] Remove Item',props<{ id:number}>());exportconst clearCart =createAction('[Cart] Clear');
// cart.reducer.tsimport{ createReducer, on }from'@ngrx/store';import{ addItem, removeItem, clearCart }from'./cart.actions';import{ CartItem }from'./cart.model';exportinterfaceCartState{ items: CartItem[];}exportconst initialState: CartState ={ items:[]};exportconst cartReducer =createReducer( initialState,on(addItem,(state,{ item })=>{const existing = state.items.find(i => i.id === item.id);if(existing){return{...state, items: state.items.map(i => i.id === item.id ?{...i, quantity: i.quantity + item.quantity }: i )};}return{...state, items:[...state.items, item]};}),on(removeItem,(state,{ id })=>({...state, items: state.items.filter(item => item.id !== id)})),on(clearCart, state =>({...state, items:[]})));
// component.tsimport{ Component }from'@angular/core';import{ Store }from'@ngrx/store';import{ Observable }from'rxjs';import{ addItem }from'./cart.actions';import{ CartState }from'./cart.reducer';@Component({ selector:'app-cart', template:` <div *ngFor="let item of items$ | async"> {{ item.name }} - {{ item.price }} </div> `})exportclassCartComponent{ items$: Observable<CartItem[]>;constructor(private store: Store<{ cart: CartState }>){this.items$ =this.store.select(state => state.cart.items);}addToCart(item: CartItem){this.store.dispatch(addItem({ item }));}}

14. 测试

示例:组件测试

// app.component.spec.tsimport{ TestBed }from'@angular/core/testing';import{ AppComponent }from'./app.component';describe('AppComponent',()=>{beforeEach(async()=>{await TestBed.configureTestingModule({ declarations:[AppComponent]}).compileComponents();});it('should create the app',()=>{const fixture = TestBed.createComponent(AppComponent);const app = fixture.componentInstance;expect(app).toBeTruthy();});it('should increment count',()=>{const fixture = TestBed.createComponent(AppComponent);const component = fixture.componentInstance;const initialCount = component.count; component.increment();expect(component.count).toBe(initialCount +1);});it('should render title',()=>{const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges();const compiled = fixture.nativeElement;expect(compiled.querySelector('h1').textContent).toContain('我的 Angular 应用');});});

示例:服务测试

// user.service.spec.tsimport{ TestBed }from'@angular/core/testing';import{ HttpClientTestingModule, HttpTestingController }from'@angular/common/http/testing';import{ UserService }from'./user.service';describe('UserService',()=>{let service: UserService;let httpMock: HttpTestingController;beforeEach(()=>{ TestBed.configureTestingModule({ imports:[HttpClientTestingModule], providers:[UserService]}); service = TestBed.inject(UserService); httpMock = TestBed.inject(HttpTestingController);});afterEach(()=>{ httpMock.verify();});it('should fetch users',()=>{const mockUsers =[{ id:1, name:'张三', email:'[email protected]'},{ id:2, name:'李四', email:'[email protected]'}]; service.getUsers().subscribe(users =>{expect(users.length).toBe(2);expect(users).toEqual(mockUsers);});const req = httpMock.expectOne('/api/users');expect(req.request.method).toBe('GET'); req.flush(mockUsers);});});

总结

Angular 是一个功能强大的前端框架,主要特点包括:

  1. 组件化架构 - 可复用的组件系统
  2. 依赖注入 - 自动管理依赖关系
  3. 模块化 - 组织代码结构
  4. 路由系统 - 单页应用导航
  5. 响应式编程 - RxJS 处理异步操作
  6. 双向数据绑定 - 简化数据流
  7. 指令系统 - 扩展 HTML 功能
  8. 表单处理 - 强大的表单验证
  9. HTTP 客户端 - 处理 API 请求
  10. 测试支持 - 完整的测试工具

这些特性使得 Angular 非常适合构建大型、复杂的企业级应用。

下边是Angular项目结构示例

my-angular-app/
├─ angular.json # Angular CLI 配置
├─ package.json # 项目依赖与脚本
├─ tsconfig.json # TypeScript 编译配置
├─ tsconfig.app.json # 应用 TS 配置
├─ tsconfig.spec.json # 测试 TS 配置
├─ karma.conf.js # 单元测试(Karma)配置
├─ src/
│ ├─ main.ts # 应用入口(bootstrap AppModule)
│ ├─ index.html # 单页应用入口 HTML
│ ├─ styles.scss # 全局样式
│ ├─ polyfills.ts # 兼容性填充
│ ├─ environments/ # 环境变量(prod/dev)
│ │ ├─ environment.ts
│ │ └─ environment.prod.ts
│ ├─ app/
│ │ ├─ app.module.ts # 根模块
│ │ ├─ app.component.* # 根组件
│ │ ├─ core/ # 核心服务、拦截器、守卫(全局单例)
│ │ ├─ shared/ # 可复用组件、指令、管道、工具
│ │ ├─ features/ # 业务功能模块(按域拆分)
│ │ │ ├─ home/ # 示例功能模块
│ │ │ ├─ users/ # 示例功能模块
│ │ │ └─ … # 其它业务域
│ │ ├─ services/ #(可选)跨功能通用服务
│ │ ├─ models/ #(可选)接口/类型定义
│ │ ├─ guards/ #(可选)路由守卫
│ │ ├─ interceptors/ #(可选)HTTP 拦截器
│ │ ├─ store/ #(可选)状态管理(NgRx、Akita 等)
│ │ └─ app-routing.module.ts # 根路由
│ └─ assets/ # 静态资源(图片、字体、i18n 等)
└─ e2e/ # 端到端测试(如使用 Cypress/Protractor)

Read more

SLAM Toolbox:工业级机器人定位与建图解决方案

SLAM Toolbox:工业级机器人定位与建图解决方案 【免费下载链接】slam_toolboxSlam Toolbox for lifelong mapping and localization in potentially massive maps with ROS 项目地址: https://gitcode.com/gh_mirrors/sl/slam_toolbox 技术挑战与核心价值 在现代工业自动化和机器人应用中,大规模环境下的实时定位与地图构建面临着多重技术挑战:传感器噪声累积、长期运行漂移、多机器人协同通信瓶颈以及动态环境适应性不足。SLAM Toolbox作为专为工业场景设计的开源解决方案,通过模块化架构和优化算法,有效解决了这些痛点问题。 核心架构解析 分层处理架构 SLAM Toolbox采用四层架构设计,确保工业级应用的可靠性和可扩展性: 数据采集层 * 支持多种激光雷达协议,包括SICK、Hokuyo和Velodyne系列 * 兼容ROS 1和ROS 2通信标准 * 提供传感器数据质量监控和异常检测

[awesome]最新最全机器人Robotics顶会“灵巧手”(dexterous hand)的paper集合

[awesome]最新最全机器人Robotics顶会“灵巧手”(dexterous hand)的paper集合

前言 “灵巧手”(dexterous hand)通常指具有类人手结构、多自由度的末端执行器,能够进行精细的抓取与操作,而不仅仅局限于平行夹紧(如下图)。它们模仿人类手指关节和肌腱驱动,使机器人能够执行转动、重定位、穿插等复杂操作。根据结构和材料不同,灵巧手大致可分为刚性型、柔性型和混合型:刚性型采用金属或坚硬塑料结构,关节通过电机或舵机驱动,优点是定位精度高、力矩大;柔性型主要用硅胶、橡胶等软材料,可通过气动驱动或形变实现自适应抓取,天生适合对柔软或不规则物体的抓取;混合型结合刚柔两者,例如刚性骨架包裹柔性层,兼顾承力和安全性。近年来,随着增材制造和传感技术进步,灵巧手的设计趋势是结构更轻便、可拓展(如3D打印一体化设计)且集成丰富传感器,使其在保持精细操作能力的同时降低成本和复杂度。总体来看,从并联双爪等简单夹具到今天的多指柔刚结合的灵巧手,已经形成多条发展脉络,各种创新不断涌现。 在机器人学中,“灵巧手”是把感知—决策—执行闭环落实到接触尺度的关键枢纽,其重要性体现在方法论与系统层两个层面:在方法论上,灵巧手将原本“抓取—位移”的低维任务,提升为包含滚动、

基于FPGA的滤波器设计:IIR、FIR与自适应滤波器

基于FPGA的滤波器设计:IIR、FIR与自适应滤波器

基于FPGA的IIR滤波器数字滤波器无限脉冲响应verilog vhdl自适应滤波器实物FIR抽取内插上下变频CIC滤波器 如果需要上述滤波器或者其他滤波器都可以右下角加好友加好友定制。 本设计是基于FPGA的IIR滤波器,VERILOG HDL和VHDL的程序都有,下面图示的滤波器设计指标是8阶的低通滤波器,采样率是1M HZ,截止频率是100K HZ可以根据你们的要求定制不同指标的滤波器; FIR滤波器,自适应滤波器也可以定做 用FPGA实现的IIR滤波器的实测图。 用FPGA实现IIR滤波器的原理图。 Simulink的仿真图,滤波前的时域信号波形放在了第二栏,滤波后的时域波形放在了第一栏。 滤波前后信号的频谱图。 IIR滤波器的零极点图 第一栏是90K Hz正弦波与110K Hz正弦波再叠加一个直流量的时域混合波形,第二栏是时域波形的频谱,从频谱中可以清晰看到三个频率分量。 滤波器最终输出结果的时域与频域波形。 simulink仿真模型。 最近在研究基于FPGA的滤波器设计,发现这玩意儿真的很有意思,今天就来和大家分享分享。咱们这次主要聚焦于IIR滤波器,当然FIR滤波器和自

FPGA 面试题目汇总含解析,FPGAer 上岸必备!

FPGA 面试题目汇总含解析,FPGAer 上岸必备!

每到招聘季,很多做 FPGA 的同学都会有同一个感受: 项目能做,代码能写,一到面试却被问得很散、很细、很杂。 本质原因只有一个: FPGA 面试问的不是“你会不会写代码”,而是“你是否真的理解硬件行为”。 这篇文章整理了一批 FPGA 面试中高频出现的经典问题,覆盖 基础语法、时序设计、接口、调试、工程经验 等方向,每道题都配有工程视角的解析,适合: * 校招 / 社招 FPGA 面试前系统复习 * 查漏补缺,验证自己“到底懂没懂” * 面试前快速过一遍,避免低级失分 一、Verilog / HDL 基础高频题 1. 阻塞赋值(=)和非阻塞赋值(<=)的区别? 参考答案: * 阻塞赋值(=) * 按顺序执行 * 常用于组合逻辑 * 非阻塞赋值(