Reputation: 6460
I have a simple and common scenario and I can't get my head around the correct way to do this with the new HttpClient
. I'm coming from Angular 1 and chainable then()
promises and not sure how to achieve this with Observables. (Would be great if Tour of Heroes tutorial was updated to reflect the shift onto HttpClient).
I have a main app.component.ts
. This component will call a AuthenticationService.login()
in authentication.service.ts
.
AuthenticationService
serves two purposes:
The code is ideally very simple (note: this is just contrived pseudocode)
app.component.ts:
export class AppComponent {
loggedIn: boolean = false;
constructor(private authenticationService: AuthenticationService) {
}
login() {
// I want to do this but don't know how
this.authenticationService.login(this.userLoginForm.email, this.userLoginForm.password).then(function(loggedIn) {
this.loggedIn = loggedIn;
}
}
}
authentication.service.ts:
@Injectable()
export class AuthenticationService {
user: User;
constructor(private http: HttpClient) {
// do stuff
}
// Recommendations I'm reading tend to prefer using Observables
// instead of Promises. But I like the `then()` chaining that
// I'm not sure how to do with Observables
// Return what here? Observable<boolean> ?
login(email, password): Observable<boolean> {
const url = `http://127.0.0.1/token/auth`;
// post() returns a LoginResponse that we subscribe to and handle here,
// but I want to return a promise-like object that will resolve to
// a boolean
return this.http.post<LoginResponse>(url, {email: email, password: password})
.subscribe(
loginResponse => {
if (loginResponse.error) {
// set user to a new unauthenticated user
this.user = new User();
} else {
this.user = JSON.parse(loginResponse.data.user) as User;
}
localStorage.setItem('currentUser', JSON.stringify(this.user));
// returning a boolean here to the caller would be nice
return this.user.id != null;
}
}
);
);
}
What am I missing here? Isn't this trivial?
Arguably, there is no need to return a boolean here. app.component.ts
can just read AuthenticationService.user to know if the user is logged in or not? Is that the way to do it?
More generally though, there surely must be a way for a service to process data returned from a server and then resolve promises that the calling component is waiting on. How does one do this?
Upvotes: 1
Views: 1485
Reputation: 13346
You can use Obervable.map to transform things into what your service should return (you want an observable of boolean here). Use observable everywhere so change .then into .subscribe in your component.
// Your service:
@Injectable()
export class AuthenticationService {
user: User;
constructor(private http: HttpClient) {
// do stuff
}
login(email, password): Observable<boolean> {
const url = `http://127.0.0.1/token/auth`;
return this.http.post<LoginResponse>(url, { email: email, password: password })
.map(loginResponse => {
this.user = loginResponse.error ? new User() : (JSON.parse(loginResponse.data.user) as User);
localStorage.setItem('currentUser', JSON.stringify(this.user));
return this.user.id != null;
}
}
}
// Your component
export class AppComponent {
loggedIn: boolean = false;
constructor(private authenticationService: AuthenticationService) { }
login() {
this.authenticationService.login(this.userLoginForm.email, this.userLoginForm.password).subscribe(loggedIn => this.loggedIn = loggedIn);
}
}
Upvotes: 1
Reputation: 68645
You can convert your Observable
into the Promise
using toPromise() function. Now your login
will return Promise
, not an Observable
and you can chain it via then
s.
login(email, password) : Promise {
const url = `http://127.0.0.1/token/auth`;
return this.http.post<LoginResponse>(url, {email: email, password: password})
.toPromise().then(loginResponse => {
this.user = loginResponse.error ? this.user = new User() :
JSON.parse(loginResponse.data.user) as User;
localStorage.setItem('currentUser', JSON.stringify(this.user));
return this.user.id !== null;
});
}
Upvotes: 0