I am using Angular CLI 1.5.4 / Angular 5.0.4 (starting code was generated by Angular CLI). The application's state is highly dependent on the current user's meta data and doing most of my HTTP request in the service classes require me to pass URL variables, like /api/company/{companyId}/some-resource/1
or /api/users/{userId}/ui-settings/some-ui-option
.
My approach: The user's object stored using a PrincipalService
after login and I can ask for it anywhere in the app (PrincipalService.getUser() : Promise<UserIdentity>
).
The user is returned as a Promise
, because I might actually need to send an Http request to load the user data. An example service which use the user's id:
@Injectable()
export class UserUiSettingsService {
constructor(private http: HttpClient,
private principal: PrincipalService) { }
private urlUserGroupingTableUISettings(): Promise<string> {
return new Promise<string>((resolve, reject) => {
this.principal.getUser().then(
user=> resolve(`${environment.httpBaseUrl}users/${user.id}/ui-settings/document-path-grouping`),
error=>reject(error)
);
});
}
getUsersGroupingTableUISettings() : Promise<DocumentPathTableUISettings> {
return new Promise<DocumentPathTableUISettings>((resolve, reject) => {
this.urlUserGroupingTableUISettings().then(
url=> this.http.get<DocumentPathTableUISettings>(url).toPromise().then(
data=>resolve(data),
error=>reject(error)
),
error=>reject(error)
);
});
}
...
}
So to actually call the http method inside getUsersGroupingTableUISettings()
, I need to make a new Promise and first resolve the User object and then I can start the request.
My problem with this is the nesting, do I really need to nest these request and wait for each success in such an ugly way? Can I do this in a more convenient way (just imagine my approach If I need to load 3-4 different things before and not just the user's, but maybe some Company data)?
There are three approaches you can use:
Observable.mergeMap
and .map
operatorCode samples for the first two:
1.
private async urlUserGroupingTableUISettings(): Promise<string> {
const user = await this.principal.getUser();
return `${environment.httpBaseUrl}users/${user.id}/ui-settings/document-path-grouping`;
}
getUsersGroupingTableUISettings() : Promise<DocumentPathTableUISettings> {
const url = await this.urlUserGroupingTableUISettings()
return await this.http.get<DocumentPathTableUISettings>(url).toPromise();
}
2.
private urlUserGroupingTableUISettings(): Promise<string> {
return this.principal.getUser().then(
user=>`${environment.httpBaseUrl}users/${user.id}/ui-settings/document-path-grouping`
);
}
getUsersGroupingTableUISettings() : Promise<DocumentPathTableUISettings> {
return this.urlUserGroupingTableUISettings()
.then(
url=>this.http.get<DocumentPathTableUISettings>(url).toPromise()
);
}
3.
With using observables, the easiest one is:
getUsersGroupingTableUISettings() : Observable<DocumentPathTableUISettings> {
return this.principalService.getUserObservable().flatMap(
user=>this.http.get<DocumentPathTableUISettings>(`${environment.httpBaseUrl}users/${user.id}/ui-settings/document-path-grouping`)
);
}