2

I'm developing an Angular app and I use Rxjs, Observables and all the stuff to transfer data from my Data Access Layer to components.

I have a Service which gets Bootstrapping data (named B). Another service (named A) fetches these data and gives them to a component through a Subject.

My goal is to keep these data in my service A but only when I get the data the first time. So, I will use a Promise.

I need my Promise to "subscribe" to the Subject and then "unsubscribe" directly.

I tried the a BehaviorSubject, A ReplaySubject but the Promise never gets called...

The Bootstrap Service

export class BService {
  propsFetched: Subject<BootstrapProperties> = new Subject<BootstrapProperties>();

  constructor(private httpClient: HttpClient) { //... }

  init() {
    this.fetchBootstrapProperties().then(
      (res) => {
        // ...
        this.propsFetched.next({ ...res });
      }
    );
  }

  private fetchBootstrapProperties(): Promise<BootstrapProperties> {
    const url = UrlResolver.resolveUrl(UrlResolver.EndPoint.BOOTSTRAP);
    return this.httpClient.get<BootstrapProperties>(url).toPromise();
  }

  getDefaultData(): Observable<Data> {
    return this.propsFetched.pipe(map(data => {
      // Some data computation
      return data;
    }));
  }
}

The service which gets data and transfers them to components

export class AService {
  sub$ = new BehaviorSubject<Data>();

  constructor(private bService: BService) {
    // This one works, it's used by my component.
    this.sub$ = this.bService.getDefaultData() as BehaviorSubject<Data>;

    // Now, I want to "keep a copy" of these data, ... But my promise never fires.
    this.sub$.toPromise().then(d => console.log(d));
}

Module and Bootstrapping config

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    // ...
  ],
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: init,
      deps: [BService],
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

export function init(bService: BService) {
  return () => bService.init();
}
Canopy
  • 51
  • 7
  • Why does fetchBootstrapProperties returns a Promise? Can't you just return an Observable? Also, with your snippet, you never call init() and the promise is never called. – Pierre R-A Jan 04 '19 at 14:35
  • fetchBootstrapProperties will be called once, I call the `init()` method at the application bootstrap – Canopy Jan 04 '19 at 14:39
  • You can use an operator such as [from](https://www.learnrxjs.io/operators/creation/from.html) to turn a `Promise` into an `Observable` – Alexander Staroselsky Jan 04 '19 at 14:55

1 Answers1

0

I would suggest keeping using Observables. There is the pipe operator share that share the result of the observable to everyone subscribing to this Observable. The call is made only once.

export class BService {
  public fetchBootstrapProperties$: Observable<BootstrapProperties>;

  constructor() {
    this.fetchBootstrapProperties$ = this.httpClient.get<BootstrapProperties>(url).pipe(
      map(data => {
        // computation
        // return data
      }),
      share()
    );
  }
}

export class AService {
  constructor(bService: BService) {
    this.bService.fetchBootstrapProperties$.subscribe(data => {
      // get your data
    );
  }
}
Pierre R-A
  • 509
  • 9
  • 13