BehaviorSubject未触发发射:跨组件共享服务DateTime数据问题
Let's walk through the problems in your code and fix them one by one — these issues are why your Observable isn't triggering and your HeaderComponent can't get the date value:
1. Critical Spelling Error
You misspelled BehaviorSubject as BahaviorSubject in your service. This is a hard error that prevents the subject from working at all — TypeScript can't resolve the incorrect class name.
2. Broken RxJS Pipe Syntax
Your pipe and tap implementation has syntax mistakes:
- You didn't wrap the pipe operators in parentheses properly
- The
tapcallback uses an incorrect object syntax, and you tried to referencecontentinstead ofthis.content(or better yet, use theresparameter directly) - The
catchErrorplacement was wrong
3. Typo in Component Constructor
MainComponent has constaructor instead of constructor, which breaks service injection.
4. Subscription Best Practices
While not a direct cause of the emission failure, subscribing in the component constructor isn't ideal (use ngOnInit instead), and you should always clean up subscriptions to avoid memory leaks.
Corrected Code
MyService
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable, throwError } from 'rxjs'; import { tap, catchError } from 'rxjs/operators'; // Ensure the service is a singleton (critical for shared state) @Injectable({ providedIn: 'root' }) export class MyService { content: any; // Fixed spelling, and set initial value to null for clarity timeOfExecutionSubject = new BehaviorSubject<Date | null>(null); timeOfExecutionSubjectChanged: Observable<Date | null> = this.timeOfExecutionSubject.asObservable(); constructor(private httpClient: HttpClient){} getData() { return this.httpClient.get<MyModel>('<httpGetUrl>') .pipe( tap(res => { this.content = res; // Directly use res.RelevantDateTime instead of relying on this.content this.timeOfExecutionSubject.next(new Date(res.RelevantDateTime)); }), // Modern throwError uses a factory function catchError(error => throwError(() => error)) ); } getRelevantDateTime() { return this.timeOfExecutionSubjectChanged; } } // Define your data model for type safety interface MyModel { RelevantDateTime: string | Date; }
MainComponent
import { Component, OnInit } from '@angular/core'; import { MyService } from './my.service'; @Component({ selector: 'app-main', template: '<p>Main Component</p>' }) export class MainComponent implements OnInit { // Fixed constructor spelling constructor(private myService: MyService ){} ngOnInit() { this.myService.getData().subscribe({ next: res => this.setData(res), error: err => console.error('Failed to fetch data:', err) }); } private setData(data: any) { // Add your data handling logic here } }
HeaderComponent (With Subscription Cleanup)
import { Component, OnInit, OnDestroy } from '@angular/core'; import { MyService } from './my.service'; import { Subscription } from 'rxjs'; import * as moment from 'moment'; @Component({ selector: 'app-header', template: '<p>Execution Time: {{ executionDateTime || "Loading..." }}</p>' }) export class HeaderComponent implements OnInit, OnDestroy { executionDateTime: string | null = null; private timeSubscription?: Subscription; constructor(private myService: MyService) {} ngOnInit() { // Subscribe in ngOnInit instead of constructor for better timing this.timeSubscription = this.myService.getRelevantDateTime().subscribe(result => { if (result) { this.executionDateTime = moment(result).format("HH:mm"); } }); } ngOnDestroy() { // Clean up subscription to prevent memory leaks this.timeSubscription?.unsubscribe(); } }
Bonus: Use Async Pipe (Even Better!)
For a cleaner approach that avoids manual subscription management, use Angular's async pipe in HeaderComponent:
// HeaderComponent with Async Pipe import { Component } from '@angular/core'; import { MyService } from './my.service'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import * as moment from 'moment'; @Component({ selector: 'app-header', template: '<p>Execution Time: {{ executionDateTime$ | async || "Loading..." }}</p>' }) export class HeaderComponent { executionDateTime$: Observable<string | null>; constructor(private myService: MyService) { this.executionDateTime$ = this.myService.getRelevantDateTime().pipe( map(result => result ? moment(result).format("HH:mm") : null) ); } }
This automatically handles subscription cleanup and is more idiomatic Angular.
内容的提问来源于stack exchange,提问作者Guy E

