Решение для этого является основным, вы должны использовать наиболее распространенные функции Angular. Я проведу вас через процесс мышления и покажу вам пример кода.
Процесс мышления:
Проблема: Нам нужно знать, вошел ли пользователь в систему постоянно или нет.
Решение: У нас будет служба, которая сообщает нам, вошел ли пользователь в систему или нет
Проблема: Панель навигации должна полагаться на статус аутентификации пользователя
Решение: Мы будем использовать статус, возвращаемый службой аутентификации, для условного отображения одного набора элементов или другого набора элементов на основе статуса аутентификации пользователя
Проблемы на уровне кода:
У вас есть определенные проблемы в вашем коде, которые затруднят вашу жизнь для дальнейшего развития других функций, которые зависят от статуса аутентификации.
Я написал два шага в том, как улучшить ваш код, этот первый просто улучшает поток ваших данных и качество кода. Второй шаг - завершение исправленного кода с более динамичным потоком данных.
Шаг 1
Сервис
У нас в службе аутентификации будет переменная, которая сообщит нам, вошел ли пользователь в систему или нет:
private isUserLoggedIn: boolean = false;
Нам нужно переместить всю логику аутентификации в Сервис аутентификации . Поскольку у меня нет кода для this.auth.loginUser(formData)
, я буду вызывать его сам из новой службы аутентификации, но учтите, что код этой функции должен быть в нашей новой функции login .
Кроме того, нет необходимости сохранять HTTP-вызов для входа в систему как наблюдаемый, поскольку вы получаете только один ответ, поэтому мы можем преобразовать его в обещание с помощью .toPromise () .
Функция входа в систему, которая вызывает API, будет выглядеть следующим образом:
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
Мы также хотим проверить, вошел ли пользователь в систему, проверив localStorage (в случае, если мы хотим, чтобы пользователю не приходилось входить в систему после каждого обновления):
Двойное отрицание !!
говорит нам, является ли значение истинным или ложным, поэтому если у нас есть что-то на ключе loggedUser
в localStorage, мы примем это, когда пользователь вошел в систему
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
Нам также потребуется функция входа в систему, которую мы вызываем при нажатии кнопки входа в систему (мы вызываем ее из службы через компонент):
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
И чтобы завершить его, мы сначала проверим, вошел ли пользователь в систему (мы делаем это, вызывая isAlreadyLoggedIn () в конструкторе службы. Также у нас будет публичная функция, с помощью которой мы можем проверить, если пользователь уже вошел в систему:
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
Полный сервис выглядит следующим образом:
@Injectable()
export class AuthService {
private isUserLoggedIn: boolean = false;
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
// Use this function to check if the user is already logged in
// Use this function to login on the server
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
}
Компонент входа:
Это проверит, вошел ли пользователь в систему при инициализации, если это так, тогда мы перенаправим пользователя на страницу профиля, в противном случае мы покажем форму входа. (HTML остается таким же, как у вас, я бы также добавил ошибку в тег span
). Обратите внимание, что в вашем файле login.ts отсутствуют свойства, я только что выполнил часть аутентификации, добавив переменные, связанные с формой, чтобы сделать компонент полным и функциональным.
@Component({
selector: 'app-login'
})
export class LoginComponent implements OnInit {
public isLoggedIn: boolean = false;
public error: string = '';
constructor(authService: AuthService, router: Router) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
if (this.isLoggedIn) {
this.router.navigate(['user-profile']);
}
}
loginUser(event) {
const formData = event.value;
this.authService.login(formData)
.then(res => this.router.navigate(['user-profile']))
.catch(error => this.error = error);
}
}
Компонент навигации:
Компонент получает статус входа в систему пользователя и соответственно отображает его элементы:
@Component({
selector: 'app-nav'
})
export class NavComponent implements OnInit {
public isLoggedIn: boolean = false;
constructor(authService: AuthService) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
}
}
Шаблон навигации:
ng-template
- это контейнер, который мы собираемся показать, если пользователь не вошел в систему.
<ul *ngIf="isLoggedIn; else notLoggedIn">
<li>Home</li>
<li>Profile</li>
<li>Log Out</li>
</ul>
<ng-template #notLoggedIn>
<ul>
<li>Home</li>
<li>Log In</li>
</ul>
</ng-template>
Этот подход будет основным, с использованием перенаправлений.
Шаг 2
Теперь мы можем завершить это более динамичным способом (хотя я бы лично использовал перенаправления):
Мы добавим следующие переменные в наш сервис:
private subject = new Subject();
private observable = this.subject.asObservable();
Это означает, что мы можем подписаться на observable
из любого компонента, а из сервиса мы можем передавать данные в реальном времени через subject
подписчикам наблюдаемого.Подробнее об этом, здесь
Теперь, когда мы обновляем статус входа, мы вызываем следующее:
this.subject.next(this.isUserLoggedIn);
И таким образом все подписчики будут уведомленыэтого изменения.
Нам нужна функция, которая возвращает наблюдаемое, на которое могут подписаться компоненты:
public isLoggedInObservable(): Observable<boolean> {
return this.observable;
}
И все, что осталось, это подписаться на это наблюдаемое из компонентов, которые нуждаютсяАктуальные обновления, касающиеся статуса аутентификации, в нашем случае, компонента nav (в пределах ngOnInit):
this.authService.isLoggedInObservable.subscribe(isLoggedIn => this.isLoggedIn = isLoggedIn);
Вот так выглядит финальный сервис:
@Injectable()
export class AuthService {
private isUserLoggedIn: boolean = false;
private subject = new Subject();
private observable = this.subject.asObservable();
constructor() {
// On initialization, check whether the user is already logged in or not
this.isUserLoggedIn = this.isAlreadyLoggedIn()
}
public login(formData): Promise<any> {
// If the user is logged in, send a promise resolvation, otherwise, send the promise of the apiLogin
if (this.isAlreadyLoggedIn) {
return Promise.resolve();
} else {
return this.apiLogin(formData);
}
}
public isLoggedIn(): boolean {
return this.isUserLoggedIn;
}
public isLoggedInObservable(): Observable<boolean> {
return this.observable;
}
// Check if the user is logged in by checking the localStorage
private isAlreadyLoggedIn(): boolean {
return !!localStorage.getItem('loggedUser');
}
// Use this function to check if the user is already logged in
// Use this function to login on the server
private apiLogin(formData): Promise<any> {
// the logic from your auth comes in here (use the content of this.auth.loginUser(formData) here)
// let's presume that we got the response from your 'this.auth.loginUser(formData)' here as loginObservable
return new Promise((resolve, reject) => {
this.auth.loginUser(formData);
.toPromise()
.then(data => {
// Form submit action here
if (data.userdata.resMsg === 'Login failed') {
// We clear the localStorage value, since the user is not logged in
localStorage.removeItem('loggedUser');
this.isUserLoggedIn = false;
this.subject.next(this.isUserLoggedIn);
reject('Email Address and Password do not match');
} else {
// We should update the localStorage
localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
this.isUserLoggedIn = true;
this.subject.next(this.isUserLoggedIn);
resolve();
}
})
.catch(error => {
this.isUserLoggedIn = false;
reject(error);
});
})
}
}
И вот какокончательный компонент Nav выглядит следующим образом:
@Component({
selector: 'app-nav'
})
export class NavComponent implements OnInit {
public isLoggedIn: boolean = false;
constructor(authService: AuthService) {}
ngOnInit() {
this.isLoggedIn = this.authService.isLoggedIn();
this.authService.isLoggedInObservable.subscribe(isLoggedIn => this.isLoggedIn = isLoggedIn);
}
}
Надеюсь, это проясняет, как должен выглядеть код.Напомним, что вы должны обработать все свои логины внутри службы и предоставить boolean
, который вы можете получить от любого компонента, чтобы вы знали статус аутентификации и действовали на его основе, а с помощью наблюдаемых вы будетевсегда получать последний статус.