barteloma
barteloma

Reputation: 6875

How to get ajax calls by order using rxjs

I have two service in my application.

@Injectable()
export class SettingService {
    private settings = new BehaviorSubject<any[]>([]);

    constructor(private http: HttpClient) {
        this.loadSettings();
    }

    private loadSettings() {
        this.http.get<any[]>('/api/settings')
            .subscribe((settings) => this.settings.next(settings));
    }

    getSettings() {
        return this.settings.asObservable();
    }
}

@Injectable()
export class InformationService {
    private informations = new BehaviorSubject<any[]>([]);

    constructor(private http: HttpClient) {
        this.loadSettings();
    }

    private loadInformations(appId: string) {
        this.http.get<any[]>('/api/informations/appId')
            .subscribe((informations) => this.informations.next(informations));
    }

    getInformations(appId: string) {
        return this.informations.asObservable();
    }
}

And I am using these service instances in my app controller.

@Component({
  selector: 'calc',
  templateUrl: 'calc.component.html',
  styleUrls: ['calc.component.css']
})
export class CalcComponent {
   constructor(informationService: InformationService, settingsService: SettingService){
        // settingsService.getSettings()
        // and use settings.appId to get informations.
        // informationService.getInformations(settings.appId)
   }
}

How can I call services by order? I am new at rxjs.

Upvotes: 0

Views: 61

Answers (3)

Chris Noring
Chris Noring

Reputation: 66

It sounds like you want the calls loadSettings and loadInformation to happen in a certain order? If you want them to finish at the same time then I would go with forkJoin(loadSettings(), loadInformation()), it's the analog to Promise.all. If you want things to happen in a certain order then I agree with the above answer of switchMap.

I wouldn't go so far as to say that the above architecture is not a best practice. It depends, here is a good article on the different patterns with HTTP.

Upvotes: 0

MoxxiManagarm
MoxxiManagarm

Reputation: 9124

Your approach with these BehaviorSubjects is not really best practice.

First of all, remove those any types. Create an interface.

export interface Setting {
  // whatever comes here
}
export interface Information {
  // whatever comes here
}

You don't need a service for every API endpoint, let's create only 1 here. You include both endpoints in this service. They return the Observable.

@Injectable()
export class MyService {
    constructor(private http: HttpClient) {}

    public loadSettings(): Observable<Setting[]> {
        return this.http.get<Setting[]>('/api/settings');
    }

    private loadInformations(appId: string): Observable<Information[]> {
        return this.http.get<Information[]>('/api/informations/appId');
    }
}

Then in your Component you can do something like this:

@Component({
  selector: 'calc',
  templateUrl: 'calc.component.html',
  styleUrls: ['calc.component.css']
})
export class CalcComponent {
   settings$: Observable<Setting[]>;
   informations$: Observable<Information[]>;

   constructor(private myService: MyService){
        this.settings$ = myService.loadSettings();
        this.informations$ = this.settings$.pipe(
          switchMap(settings => myService.loadInformations(myAppId)), // whereever myAppId comes from
        );
   }
}

Subscribe to it in your template with async pipe.

Upvotes: 1

Lia
Lia

Reputation: 11982

You can use rxjs switchMap operator:

settingsService.getSettings().pipe(switchMap(res1 => {
    informationService.getInformations(res1.appId)
  }).subscribe(res2 => {
    console.log(res2)
  })

Upvotes: 0

Related Questions