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

Angular组件间服务数据无法共享问题求助

问题描述

我创建了如下DataAccessService服务:

@Injectable({
  providedIn: 'root',
})
export class DataAccessService {
  constructor(private http: HttpClient) {}

  @Output() queryParamsChange: EventEmitter<any> = new EventEmitter();

  _queryParams: any = null;

  set queryParams(value: any) {
    this._queryParams = value;
    this.queryParamsChange.emit(this._queryParams);
  }

  get queryParams(): any {
    return this._queryParams;
  }

该服务在app-root、app-home、app-study三个独立式组件中注入使用,Angular版本为17.3.6,且已在app.config.ts中注册该服务:

export const appProviders = [
  provideRouter(routes),
  provideHttpClient(),
  provideAnimations(),
  importProvidersFrom(
    TranslateModule.forRoot({
      defaultLanguage: 'fr',
      loader: {
        provide: TranslateLoader,
        useFactory: createTranslateLoader,
        deps: [HttpClient],
      },
    }),
    KeycloakAngularModule,
    HttpClientModule
  ),
  { provide: APP_BASE_HREF, useValue: environment.baseHref },
  { provide: LOCALE_ID, useValue: 'fr' },
  {
    provide: HTTP_INTERCEPTORS,
    useClass: CacheInterceptor,
    multi: true,
  },
  {
    provide: APP_INITIALIZER,
    useFactory: initializeKeycloak,
    multi: true,
    deps: [KeycloakService],
  },
  { provide: AuthService, deps: [KeycloakService] },
  KeycloakService,
  DataAccessService,
];

export const appConfig: ApplicationConfig = {
  providers: appProviders,
};

各组件注入方式如下:

App组件

export class AppComponent implements OnInit {
  ...
  constructor(
    private dataService: DataAccessService,
    private router: Router,
  ) {
  ...
  } 

Study组件

export class StudiesComponent implements OnInit, OnDestroy {

  routeNames: string[] = ['studies.toolbar.routeName'];
  queryParamsChange: Subscription;

  constructor(private dataService: DataAccessService) {}

  ngOnInit(): void {
    console.log('ON_INIT StudiesComponent');
    console.log(
      'queryParams : ' + JSON.stringify(this.dataService._queryParams)
    );

Home组件

export class HomeComponent implements OnInit {
  ...
  constructor(
    private dataService: DataAccessService,
    private router: Router
  ) {}

  onClick(route: string, value: any): void {
    console.log(value);
    this.dataService.queryParams = value;
    this.router.navigate([route]);
  }

问题现象

点击Home组件的按钮时,value已成功传入服务(日志验证有效),并通过router.navigate跳转至Study页面,但在StudiesComponent的ngOnInit中读取this.dataService.queryParams却返回Null。新增Test组件读取服务数据同样返回Null:

import { Component, OnInit } from '@angular/core';
import { DataAccessService } from '../services/data-access.service';

@Component({
  selector: 'app-test',
  standalone: true,
  imports: [],
  templateUrl: './test.component.html',
  styleUrl: './test.component.scss',
})
export class TestComponent implements OnInit {
  constructor(private dataService: DataAccessService) {}

  ngOnInit(): void {
    console.log('ON_INIT TestComponent');
    const queryParams = this.dataService.queryParams;
    console.log('queryParams : ' + JSON.stringify(queryParams));
  }
}
问题原因及解决方案

核心原因

你同时通过两种方式提供了DataAccessService:

  1. 服务本身使用@Injectable({ providedIn: 'root' }),Angular会自动将其注册为根级单例服务;
  2. 又在appConfig.providers中手动添加DataAccessService,导致Angular创建第二个独立实例。

Home组件注入的是第一个实例(root提供的),而Study/Test组件注入的是第二个实例(手动注册的),自然读取不到之前设置的值。

解决方案

只需保留一种服务提供方式即可,推荐移除appConfig.providers中的DataAccessService条目——providedIn: 'root'是Angular官方推荐的单例服务注册方式,更简洁且避免冲突。

修改后的appProviders:

export const appProviders = [
  provideRouter(routes),
  provideHttpClient(),
  provideAnimations(),
  importProvidersFrom(
    TranslateModule.forRoot({
      defaultLanguage: 'fr',
      loader: {
        provide: TranslateLoader,
        useFactory: createTranslateLoader,
        deps: [HttpClient],
      },
    }),
    KeycloakAngularModule,
    HttpClientModule
  ),
  { provide: APP_BASE_HREF, useValue: environment.baseHref },
  { provide: LOCALE_ID, useValue: 'fr' },
  {
    provide: HTTP_INTERCEPTORS,
    useClass: CacheInterceptor,
    multi: true,
  },
  {
    provide: APP_INITIALIZER,
    useFactory: initializeKeycloak,
    multi: true,
    deps: [KeycloakService],
  },
  { provide: AuthService, deps: [KeycloakService] },
  KeycloakService,
  // 移除该行:DataAccessService,
];

额外优化建议

  1. 服务中的_queryParams不应让组件直接访问(比如Study组件中直接调用this.dataService._queryParams),始终通过get queryParams()获取,保证封装性;
  2. @Output()装饰器是为组件输出属性设计的,服务中直接使用EventEmitter即可,无需添加该装饰器。

修改后的服务:

@Injectable({
  providedIn: 'root',
})
export class DataAccessService {
  constructor(private http: HttpClient) {}

  // 移除@Output()
  queryParamsChange: EventEmitter<any> = new EventEmitter();

  private _queryParams: any = null; // 改为private,禁止外部直接访问

  set queryParams(value: any) {
    this._queryParams = value;
    this.queryParamsChange.emit(this._queryParams);
  }

  get queryParams(): any {
    return this._queryParams;
  }

完成上述修改后,所有组件都会注入同一个服务实例,数据即可正常共享。

内容的提问来源于stack exchange,提问作者STEPHANE

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 11:05:54