Как связать данные между сервисами и компонентами в режиме реального времени.
давайте предположим isAuthenticated общедоступная переменная для службы аутентификации, которая влияет на некоторое представление в компоненте. Мой вопрос: как подписаться на переменную isAuthenticated?
Оказание услуг:
import { Injectable } from '@angular/core';
@Injectable()
export class Authentication {
isAuthenticated:boolean = false;
login() {
localStorage.setItem('access_token', 'true');
this.isAuthenticated = true;
}
}
Компонент:
...
@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>
...
Судя по текущему потоку выше, привязка работает хорошо, но не в реальном времени.
Итак, каков наилучший подход:
1- Создайте наблюдаемую внутри службы аутентификации, чтобы подписаться на нее внутри компонента.
2- Связывание следующим образом:
...
<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.
Спасибо.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я предполагаю, что из вашего тега вы используете Ionic framework? Можно ли это сделать с помощью событий?
// first page (publish an event when a user is created)
constructor(public events: Events) {}
createUser(user) {
console.info('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.info('Welcome', user, 'at', time);
});
}
Пример кода взят из: https://ionicframework.com/docs/v3/api/util/Events/
можно ли создать событие в провайдере (сервисе)?
Это должно быть возможно, но я не уверен, что это лучшая практика, что вызывает метод «логин»? Это можно изменить так, чтобы он возвращал наблюдаемое или обещание, а затем из этого вызова событие Ionic (при условии, что то, что вызывает его, находится не на той же странице, с которой вы вызываете «вход», и в этом случае событие не будет необходимо ).
Я не думаю, что вы можете изменить значение события, например ... next (false) или что-то в этом роде.
Я бы рекомендовал использовать BehaviorSubject. Это Observable, поэтому вы можете подписаться на него, но вы также можете контролировать, когда он выдает новые значения, вызывая behaviorSubject.next(newValue). При создании BehaviorSubject вы должны передать ему начальное значение. В вашем случае это 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)
}
}
или вы можете подписаться в html с помощью 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>
В отличие от обычного Observable, когда вы вызываете subscribe для BehaviorSubject, функция, которую вы передали в качестве аргумента для подписки, будет немедленно выполнена. Это связано с тем, что BehaviorSubject всегда имеет значение. Вы можете получить к нему доступ с помощью this.authService.isAuthenticated.value, но здесь это не очень полезно.
Как импортировать BehaviorSubject?
какую версию RxJS вы используете?
для RxJS v6+ вы можете импортировать его вот так import { BehaviorSubject } from 'rxjs';
Используйте RXJS. Использование BehaviourSubjects позволяет вам передавать состояние и подписываться на изменения состояния в нескольких компонентах, которые внедряют службу и имеют начальное состояние. При определении BehaviourSubject вы также должны определить начальное значение, которое здесь равно false. Все, что вам нужно сделать, это вызвать .next(true) в BehaviourSubject для отправки состояния, как показано ниже:
...
@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);
}
}
Использование метода get в вашей службе позволяет вам возвращать наблюдаемое без публичного раскрытия методов BehaviourSubject.
Затем в вашем компоненте:
...
@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)
}
}
На данный момент я использую это решение:
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
}
}
}
Даже если я не уверен, что это хороший способ.
Не выполняйте запросы в конструкторе (т. е. вызывайте службу для проверки подлинности). Вместо этого внедрите
OnInitв свой компонент и сделайте это там.