You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中如何利用Signals实现组件间数据传递(含API请求场景)

Angular中如何利用Signals实现组件间数据传递(含API请求场景)

我完全懂你现在的困惑——用Signals处理API数据时,既想遵循最佳实践,又要保证组件间数据能实时同步更新,确实容易卡壳。咱们一步步拆解问题,帮你把数据流理顺:

一、先明确最佳实践:把信号放在服务里

服务是Angular的单例对象,天生适合存放共享状态。把航班数据的信号放在FlightService里,所有组件都能共享同一数据源,更新一次就全量同步,完美解决组件间数据传递的问题。

二、改造FlightService,维护共享信号

咱们把原来的Observable逻辑和信号结合起来,让服务负责数据的获取和状态更新:

import { Injectable, signal, WritableSignal } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { catchError, retry, throwError } from 'rxjs';
import { Flight } from '../models/flight';

@Injectable({
  providedIn: 'root'
})
export class FlightService {
  private API_URL = 'http://localhost:4963/';
  
  // 私有可写信号:只允许服务内部修改状态
  private _flights: WritableSignal<Flight[]> = signal([]);
  // 暴露只读信号给组件:避免外部随意修改状态,保证数据可控
  public flights = this._flights.asReadonly();

  constructor(private http: HttpClient) {
    // 可选:在服务初始化时自动加载数据,也可以保留init方法让组件触发
    this.initFlights();
  }

  public initFlights(): void {
    this.http.get<Flight[]>(this.API_URL + 'flights')
      .pipe(
        retry(1),
        catchError(this._handleError)
      )
      .subscribe({
        next: (fetchedFlights) => {
          // API请求成功后,更新服务内的信号
          this._flights.set(fetchedFlights);
        }
      });
  }

  private _handleError(err: HttpErrorResponse) {
    console.log('err:', err);
    return throwError(() => err);
  }

  // 后续如果有修改航班的需求,比如更新、删除,都在这里写方法修改信号
  public updateFlight(updatedFlight: Flight): void {
    this._flights.update(currentFlights => 
      currentFlights.map(flight => 
        flight.flightNumber === updatedFlight.flightNumber ? updatedFlight : flight
      )
    );
  }
}

更简洁的写法:用toSignal自动转换Observable
如果你不想手动管理订阅,可以用Angular提供的toSignal操作符,直接把API的Observable转换成信号,它会自动处理订阅和取消订阅:

// 替换服务里的信号定义和init方法
import { toSignal } from '@angular/core/rxjs-interop';
import { startWith } from 'rxjs';

// ...其他代码

public flights = toSignal(
  this.http.get<Flight[]>(this.API_URL + 'flights')
    .pipe(
      retry(1),
      catchError(this._handleError),
      startWith([]) // 设置初始值为空数组
    ),
  { initialValue: [] } // 信号的初始值
);

三、改造AppComponent:消费服务的信号

现在组件不用自己维护信号了,直接注入服务,用服务提供的只读信号就行:

import { RouterOutlet } from '@angular/router';
import { AppHeaderComponent } from '../cmps/app-header/app-header.component';
import { FilterComponent } from '../cmps/filter/filter.component';
import { SelectedFlight } from '../models/selected-flight';
import { FlightListComponent } from '../cmps/flight-list/flight-list.component';
import { FlightService } from '../services/flight.service';
import { OnInit, inject } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet, AppHeaderComponent, FilterComponent, FlightListComponent],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss'
})
export class AppComponent implements OnInit {
  private flightService = inject(FlightService);
  // 直接复用服务的只读信号
  flights = this.flightService.flights;

  ngOnInit(): void {
    // 如果服务没有在构造函数自动加载数据,就在这里调用init方法
    // this.flightService.initFlights();
  }

  onSelectFlight = (flight: SelectedFlight) => {
    console.log('hi');
    console.log(flight);
  }
}

四、调整FlightListComponent:接收只读信号

子组件只需要消费数据,不需要修改状态,所以Input接收Signal<Flight[]>即可,不用WritableSignal:

import { Component, Input, Signal } from '@angular/core';
import { Flight } from '../../models/flight';
import { CommonModule } from '@angular/common';
import { FlightPreviewComponent } from '../flight-preview/flight-preview.component';

@Component({
  selector: 'flight-list',
  standalone: true,
  templateUrl: './flight-list.component.html',
  styleUrl: './flight-list.component.scss',
  imports: [CommonModule, FlightPreviewComponent]
})
export class FlightListComponent {
  // 接收只读信号,保证子组件不会意外修改状态
  @Input() flights!: Signal<Flight[]>;

  trackByFn = (idx: number, flight: Flight) => {
    console.log(flight.flightNumber);
    return flight.flightNumber;
  }
}

五、关键注意点

  1. 视图自动更新:你的flight-list.component.html里已经用了flights()来获取信号值,Angular会自动订阅信号的变化,只要信号更新,视图就会实时渲染最新数据,完全不用手动触发变更检测。
  2. 单一数据源原则:所有状态修改都通过服务的方法进行,组件只负责消费和展示数据,这样你的数据流会非常清晰,不容易出现状态不一致的问题。
  3. 避免内存泄漏:如果用手动subscribe的方式,记得在服务或组件销毁时取消订阅吗?其实用toSignal的话,Angular会自动处理订阅清理;如果是手动subscribe,可以把订阅对象存起来,在ngOnDestroy里unsubscribe。

这样改造后,你的服务会维护唯一的航班数据源,任何组件只要注入FlightService就能拿到最新的航班数据,API请求完成后信号自动更新,所有消费该信号的组件都会同步刷新视图~

备注:内容来源于stack exchange,提问作者Manoon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 15:23:07