Reputation: 3383
I'm having problems subscribing to an observer that's called in a different component.
My Angular 4 app has a navigation bar containing some of the user information. The user information is obtained from a http request to a server on login. This is managed with the LoginService. At logout we set the user to null.
logIn() {
return this.http
.get(this.apiUrl)
.map(response => response.json())
}
logOut() {
return Observable.of(null)
}
From the login component it's pretty easy to get the user details using the service.
logIn() {
this.loginService.logIn().subscribe((user) => {
console.log('Logged in')
this.user = user
console.log(this.user)
)
}
But I also need to get them from another component to set the values in the navbar. I think I need to use the navbar component to subscribe to the LoginService logIn() and logOut() functions, merge these observables, and if either of them trigger an event, update the user. But I can't subscribe to them and 2 days later I'm still stuck on this.
I've made a plunk to show the problem. When the login button is clicked, the user in the app.component should be set, and should be unset when the logout button is clicked.
Upvotes: 2
Views: 1136
Reputation: 96891
I'd just use BehaviorSubject
instead of keeping the user as a property in LoginService
. Then you can subscribe to it any time you need the current user. Logout is then just calling .next(null)
:
See you updated plnkr: https://plnkr.co/edit/soeqH4IiNhcanNqxdnLS?p=preview
LoginService
@Injectable()
export class LoginService {
private apiUrl = 'https://api.github.com/users/geraintanderson'
user$ = new BehaviorSubject(null);
constructor(
private http: Http
) {}
logIn() {
return this.http
.get(this.apiUrl)
.map(response => response.json())
.do(user => this.user$.next(user))
}
logOut() {
this.user$.next(null);
}
}
Template:
<span *ngIf="loginService.user$ | async; else noUser; let user">
Logged in as <b>{{ user.name }}</b>
</span>
<ng-template #noUser><span>Not logged in</span></ng-template>
Upvotes: 4
Reputation: 60518
A much simpler solution is to not expose an observable but instead just expose the data. I have updated your plunker here: https://plnkr.co/edit/ilfEtmxolC16vRKRiE0q?p=preview
The service:
import { Injectable } from '@angular/core'
import { Http } from '@angular/http'
import 'rxjs/add/operator/map'
@Injectable()
export class LoginService {
user;
private apiUrl = 'https://api.github.com/users/geraintanderson'
constructor(
private http: Http
) {}
logIn() {
return this.http
.get(this.apiUrl)
.map(response => response.json())
.subscribe((user) => {
console.log('Logged in')
this.user = user
console.log(JSON.stringify(this.user));
)
}
logOut() {
this.user = null;
console.log('Logged out')
}
}
The component:
import {Component, OnInit, VERSION} from '@angular/core'
import { LoginService } from './login.service'
@Component({
selector: 'my-app',
templateUrl: './src/app.component.html',
})
export class App implements OnInit {
get user() {
return this.loginService.user;
}
constructor(private loginService: LoginService) {
this.angularVersion = `Angular version ${VERSION.full}`
}
}
The html:
<div>
<span *ngIf="user">Logged in as <b>{{user.name}}</b></span>
<span *ngIf="!user">Not logged in</span>
<h2>Shared services</h2><p>Created with {{angularVersion}}</p>
<hr>
<app-login></app-login>
</div>
Upvotes: 1