How to bind data between services and components in real time way.
let's suppose isAuthenticated
a public variable for Authentication service that is affecting some view in a component. My question is how to subscribe to isAuthenticated
variable?
Service:
import { Injectable } from '@angular/core';
@Injectable()
export class Authentication {
isAuthenticated:boolean = false;
login() {
localStorage.setItem('access_token', 'true');
this.isAuthenticated = true;
}
}
Component:
...
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
private isAuthenticated:boolean = false;
constructor(public authService: Authentication) {
this.isAuthenticated = this.authService.isAuthenticated'
}
}
home.html
...
<div *ngIf="isAuthenticated">Authentication view</div>
<div *ngIf="!isAuthenticated">Unauthentication view</div>
...
By the current flow above, the binding is working well but isn't real time.
So what is the best approach:
1- Create an observable inside the Authentication service in order to subscribe to it inside the component.
2- Binding using the following way:
...
<div *ngIf="authService.isAuthenticated">Authentication view</div>
<div *ngIf="!authService.isAuthenticated">Unauthentication view</div>
...
The second approach is working well but I don't know if it is the best practice.
Thanks.
I would recommend using BehaviorSubject
. It's an Observable
, so you can subscribe to it, but you can also control when it emits new values by calling behaviorSubject.next(newValue)
. When creating BehaviorSubject you must pass inital value to it. In your case it's false
.
@Injectable()
export class Authentication {
isAuthenticated = new BehaviorSubject<boolean>(false);
login() {
localStorage.setItem('access_token', 'true');
this.isAuthenticated.next(true);
}
}
-
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
private isAuthenticated:boolean;
constructor(public authService: Authentication) {
this.authService.isAuthenticated
.subscribe(isAuthenticated => this.isAuthenticated = isAuthenticated)
}
}
or you can subscribe in html with Async Pipe
export class HomePage {
private isAuthenticated: BehaviorSubject<boolean>;
constructor(public authService: Authentication) {
this.isAuthenticated = this.authService.isAuthenticated;
}
}
-
<div *ngIf="isAuthenticated | async">Authentication view</div>
<div *ngIf="!(isAuthenticated | async)">Unauthentication view</div>
Unlike regular Observable, when you call subscribe on BehaviorSubject, the function you passed as an argument to subscribe will be immediately executed. This is because BehaviorSubject always has a value. You can access it with this.authService.isAuthenticated.value
but it's not very useful here.
I assume from your tag you are using Ionic framework? Could this maybe be done using events?
// first page (publish an event when a user is created)
constructor(public events: Events) {}
createUser(user) {
console.log('User created!')
this.events.publish('user:created', user, Date.now());
}
// second page (listen for the user created event after function is called)
constructor(public events: Events) {
events.subscribe('user:created', (user, time) => {
// user and time are the same arguments passed in `events.publish(user, time)`
console.log('Welcome', user, 'at', time);
});
}
Example code taken from: https://ionicframework.com/docs/v3/api/util/Events/
Make use of RXJS. Using BehaviourSubjects allows you to push state and subscribe to state changes throughout multiple components that inject the service and have an initial state. When defining a BehaviourSubject you must also define a starting value which here is false. All you have to do is call .next(true)
on the BehaviourSubject to push state as shown below:
...
@Injectable()
export class Authentication {
private _isAuthenticated: BehaviourSubject<boolean> = new BehaviourSubject(false);
public get isAuthenticated(): Observable<boolean> {
return this._isAuthenticated.asObservable();
}
login() {
localStorage.setItem('access_token', 'true');
this._isAuthenticated.next(true);
}
}
Using a get method on your service allows you to return an observable without publically exposing the methods on the BehaviourSubject.
Then in your component:
...
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage implements OnInit {
private isAuthenticated:boolean = false;
private readonly _authService: Authentication;
constructor(authService: Authentication) {
this._authService = authService;
}
public ngOnInit(): void {
this._authService.isAuthenticated
.subscribe((isAuthenticated) => this.isAuthenticated = isAuthenticated)
}
}
At the moment I'm using this solution:
import { DoCheck } from '@angular/core';
//...
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
private isAuthenticated:boolean = false;
constructor(public authService: Authentication) { }
ngDoCheck() {
if (this.authService.isAuthenticated)) {
//..if authenticated do this
} else {
//..if not do this
}
}
}
Even if I'm not sure this is a nice way.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.