RxJS提前赋值问题:emit/map完成前赋值导致默认值生效
问题描述
this.version始终获取到??后的默认值,原因是赋值操作执行时,.map操作尚未完成,导致后续读取this.version时仍是初始值。需要修改代码让赋值操作在emit/map完成后再执行,getVersionValues函数在app.component.ts中被调用时,emit/map还未完成,已提供完整代码。
解决方案
1. 修改VersionInfoService
将服务改为异步模式,利用RxJS数据流确保拿到版本数据后再返回结果:
import { Injectable } from '@angular/core'; import { VersionValues } from '@app-core/models/standard-filter.model'; import {ApiHelperService} from "@app-core/services/api-helper.service"; import {map, shareReplay} from "rxjs"; @Injectable({providedIn: 'root'}) export class VersionInfoService { // 用shareReplay缓存请求结果,避免重复发起API调用 userInfo$ = this.apiHelperService.getUserInfo().pipe( shareReplay(1) ); constructor( private apiHelperService: ApiHelperService ){} getVersionValues(): Observable<VersionValues> { return this.userInfo$.pipe( map(userInfo => { const versions = userInfo.versions || []; return { frontendVersion: versions?.[0] ?? "1.1.3", backendVersion: versions?.[1] ?? "1.1.3", javaVersion: versions?.[2] ?? "1.1.29", pentahoVersion: versions?.[3] ?? "1.1.3" }; }) ); } }
2. 修改AppComponent
调整按钮创建逻辑,等待异步版本数据返回后再初始化按钮:
import {AfterViewInit, Component, OnDestroy, OnInit, Type} from '@angular/core'; import {SmartSafeAny} from '@r-software/smart-ng/core/types'; import { BaseSlotComponent, BaseSlotInfoInputType, SmartBaseButton, SmartButtonSlotItem, SmartHeaderService, SmartSidebarNavigationService, SmartSlotItem } from '@r-software/smart-ng/layout'; import {filter, takeUntil} from 'rxjs/operators'; import {Observable, Subject} from 'rxjs'; import { SmartHeaderBaseSlotButtonComponent } from '@app-core/components/smart-header-base-slot-button/smart-header-base-slot-button.component'; import {SmartDialog} from '@r-software/smart-ng/dialog'; import {AccessModalComponent} from '@app-shared/components/access-modal/access-modal.component'; import {ApiHelperService} from '@app-core/services/api-helper.service'; import {SidebarHelperService} from '@app-core/services/utils/sidebar-helper.service'; import {ConfigurationService} from '@app-core/services/configuration/configuration.service'; import {SpotviewerUiWebMetaDataService} from 'meta-data/spotviewer-ui-web-meta-data.service'; import {FeatureStateService} from '@app-core/services/utils/feature-state.service'; import {ClassEnum} from '@app-core/models/enum/class.enum'; import {VersionInfoService} from "@app-core/services/utils/version-info.service"; import { VersionValues } from '@app-core/models/standard-filter.model'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit, OnDestroy, AfterViewInit { get slotItems(): SmartSlotItem { return this._slotItems; } set slotItems(value: SmartSlotItem) { this._slotItems = value; } private _slotItems!: SmartSlotItem; buttonSlotItems!: SmartButtonSlotItem[]; private baseButtons: SmartBaseButton[] = []; private destroy$: Subject<boolean> = new Subject<boolean>(); constructor( private metaDataService: SpotviewerUiWebMetaDataService, private navigationHelperService: SidebarHelperService, private sidebarService: SmartSidebarNavigationService, private versionInfoService: VersionInfoService, private configurationService: ConfigurationService, private featureStateService: FeatureStateService, private apiHelperService: ApiHelperService, private headerService: SmartHeaderService, private dialog: SmartDialog ) {} ngOnInit(): void { this.featureStateService.mapFeaturesFromToken(); this.headerService.setInfoSlot('SPOT'); this.sidebarService.register([]); this.getReportTypes(); // 等待版本数据返回后再创建按钮并设置到头部 this.versionInfoService.getVersionValues() .pipe(takeUntil(this.destroy$)) .subscribe(versionValues => { this.baseButtons = this.createBaseButton(versionValues); this.headerService.setButtonTemplateSlot(this.baseButtons); }); const [textTemplate$, _textOnly$, buttons$]: Observable<SmartSafeAny>[] = this.headerService.__slotSource__; textTemplate$ .pipe( filter((slot) => !!slot), takeUntil(this.destroy$) ) .subscribe({ next: (slot: [Type<BaseSlotComponent>, BaseSlotInfoInputType]) => { const [component, data] = slot; this._slotItems = new SmartSlotItem(component, data); } }); buttons$.pipe(takeUntil(this.destroy$)).subscribe({ next: (slots: SmartBaseButton[]) => { this.buttonSlotItems = slots.map((entry) => new SmartButtonSlotItem(SmartHeaderBaseSlotButtonComponent, entry)); } }); } ngAfterViewInit(): void { if (this.featureStateService.state.bigScrollbar) { const smartSidenavEl = document.querySelector('smart-sidenav'); const scrollEl = document.querySelector('.content-container'); if (smartSidenavEl && scrollEl) { smartSidenavEl.className += ClassEnum.BIG_SCROLLBAR; scrollEl.className += ClassEnum.BIG_SCROLLBAR; } } } ngOnDestroy(): void { this.destroy$.next(true); this.destroy$.complete(); } private getReportTypes(): void { this.navigationHelperService.sidebar$ .pipe(takeUntil(this.destroy$)) .subscribe(e => { this.sidebarService.addEntry(e); }) } // 接收版本数据参数,直接使用异步获取的版本值 private createBaseButton(versionValues: VersionValues): SmartBaseButton[] { const fe_version = `FEv${versionValues.frontendVersion}`; const be_version = `BEv${versionValues.backendVersion}`; const ja_version = `JAv${versionValues.javaVersion}`; const pe_version = `PEv${versionValues.pentahoVersion}`; return [ { text: `${ja_version}|${pe_version}|${be_version}|${fe_version}`, type: 'version', callback: () => { // this is intentional } }, { text: 'Befugnis', icon: 'key', primary: true, callback: () => { this.dialog.open(AccessModalComponent, { width: '800px', minHeight: '500px' }); } } ]; } }
关键修改说明
服务层调整:
- 移除同步
versions变量,直接通过userInfo$数据流处理异步返回的版本数据 - 使用
shareReplay(1)缓存API请求结果,避免重复调用 getVersionValues改为返回Observable<VersionValues>,确保数据就绪后再返回结果
- 移除同步
组件层调整:
- 不再在构造函数中初始化按钮,改为在
ngOnInit中等待版本数据返回后再创建 - 修改
createBaseButton方法,接收异步获取的版本数据参数,直接使用真实版本值
- 不再在构造函数中初始化按钮,改为在
内容的提问来源于stack exchange,提问作者StingerWolf
相关产品推荐
相关产品推荐

