Basically as a title states, I need to return multiple observables or maybe a results. The goal is basically to load lets say a library list and then load a books based on that library ID's. I don't want to call a service in components, instead I want all the data to be loaded before the page load.
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { UserService } from './../_services/index';
@Injectable()
export class LibraryResolver implements Resolve<any> {
constructor(private _userService: UserService) {}
resolve(route: ActivatedRouteSnapshot) {
return this._userService.getLibraryList();
}
}
How can I load library list first and then load books info for each library and return to my component?
PS: My service got this method to load by Id
this.userService.getLibraryBooks(this.library["id"]).subscribe((response) => {
...
I found a solution for this issue, maybe will help somebody, so basically I've used forkJoin
to combine multiple Observables and resolve all of them.
resolve(route: ActivatedRouteSnapshot): Observable<any> {
return forkJoin([
this._elementsService.getElementTypes(),
this._elementsService.getDepartments()
.catch(error => {
/* if(error.status === 404) {
this.router.navigate(['subscription-create']);
} */
return Observable.throw(error);
})
]).map(result => {
return {
types: result[0],
departments: result[1]
};
});
};
Now it works correctly, as intended.
You can easily resolve multiple observables using withLatestFrom
.
Here's a working demo in Stackblitz: https://stackblitz.com/edit/angular-xfd5xx
Solution below.
In your resolver, use withLatestFrom
to combine your observables:
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Library } from '../models/library.model';
import { LibraryBook } from '../models/library-book.model';
import { LibraryService } from '../services/library.service';
import { Observable } from 'rxjs';
import { withLatestFrom } from 'rxjs/operators';
@Injectable()
export class LibraryDisplayResolver implements Resolve<[Library, LibraryBook[]]> {
constructor(
private _libraryService: LibraryService,
) { }
resolve (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<[Library, LibraryBook[]]> {
const libraryId = route.params['id'];
return this._libraryService.getLibrary(libraryId).pipe(
withLatestFrom(
this._libraryService.getBooksFromLibrary(libraryId)
)
);
}
}
Make sure your resolver is set in your route with an appropriate identifier:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LibraryDisplayComponent } from './library-display.component';
import { LibraryDisplayResolver } from '../resolvers/library-display.resolver';
const routes: Routes = [
{
path: ':id',
component: LibraryDisplayComponent,
resolve: {
libraryResolverData: LibraryDisplayResolver
}
},
{
path: '',
redirectTo: '1',
pathMatch: 'full'
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LibraryDisplayRoutingModule { }
In the receiving component, you can access snapshots of both observables like so:
import { Component, OnInit } from '@angular/core';
import { LibraryBook } from '../models/library-book.model';
import { Library } from '../models/library.model';
import { ActivatedRoute } from '@angular/router';
@Component({
// tslint:disable-next-line:component-selector
selector: 'library-display',
templateUrl: './library-display.component.html',
styleUrls: ['./library-display.component.scss']
})
export class LibraryDisplayComponent implements OnInit {
library: Library;
libraryBooks: LibraryBook[];
constructor(
private route: ActivatedRoute
) { }
ngOnInit() {
this.library = this.route.snapshot.data['libraryResolverData'][0];
this.libraryBooks = this.route.snapshot.data['libraryResolverData'][1];
}
}
I have a similar situation, but I approached it slightly differently.
In your case, my understanding is you want to get the query parameter, and then based on the response, call a few more services.
The way I do that is by having a component that wraps around the others, and passes the objects they need. I subscribe to them through the route paramMap and then wrap all my other calls in a. Observalbe.forkJoin
In my wrapper component I do the following:
ngOnInit() {
this.route.params.subscribe((params) => {
if (params.hasOwnProperty('id') && params['id'] != '') {
const slug = params['slug'];
Observable.forkJoin([
this.myService.getData(id),
this.myService.getOtherData(id),
]).subscribe(
([data, otherData]) => {
this.data = data;
this.otherData = otherData;
},
error => {
console.log('An error occurred:', error);
},
() => {
this.loading = false;
}
);
}
});
Not entirely what you're after but I hope it points you in the right direction
I would suggest you use Observables, they will make your life a lot easy. Check these two articles for more details on how to achieve that using observables:
http://www.syntaxsuccess.com/viewarticle/combining-multiple-rxjs-streams-in-angular-2.0
http://blog.danieleghidoli.it/2016/10/22/http-rxjs-observables-angular/
I personally use flatMap
Good Luck.
来源:https://stackoverflow.com/questions/48146996/angular-4-how-to-return-multiple-observables-in-resolver