Обновить определенный компонент в Angular - PullRequest
0 голосов
/ 13 мая 2019

Новичок в угловой, поэтому может показаться тривиальным вопросом, однако все решения, представленные в SO, до сих пор не работали.У меня есть простой компонент входа в систему, где при отправке я перенаправляю пользователя на страницу профиля.Я могу отправить пользователя в указанный компонент, но верхняя панель навигации не обновляется автоматически, т.е. я сохранил проверку сеанса, поэтому при входе пользователя в панель навигации автоматически должна отображаться кнопка Logout вместо *Кнопка 1002 *.Мои файлы кода выглядят примерно так:

login-page.component.html

<form #loginForm="ngForm" (ngSubmit)="loginUser(loginForm)" id="loginForm" class="loginbackground">
<input ngModel #emailAddress="ngModel" type="text" autocomplete="off" placeholder="Email" id="emailAddress" name="emailAddress" />
<button type="submit" id="submit">LOGIN</button>

login-page.component.ts

@Output() refreshEvent = new EventEmitter<any>();
loginUser(event) {
// Validations. If successful, proceed

const formData = event.value;
this.auth.loginUser(formData);
  .subscribe(data => {
    localStorage.setItem('loggedUser', JSON.stringify(data.userdata));
    // Form submit action here
    if (data.userdata.resMsg === 'Login failed') {
      this.errorPopup = true;
      this.errorText = 'Email Address and Password do not match';
    } else {
      this.refreshEvent.emit();
      this.emailAvailable = true;
      this.showLogin = false;
      this.showRegister = false;
      this.router.navigateByUrl('/404', { skipLocationChange: true }).then(() =>
        this.router.navigate(['user-profile']));
    }
  });
});
}

Задача Когда я вручную обновляю страницу, панель навигации отражает изменения в соответствии с написанной логикой, которая работает нормально.Тем не менее, я хочу, чтобы это происходило, когда пользователь фактически входит в систему, и не должно быть необходимости вручную обновлять страницу.

Что я пробовал

  • Я пытался использовать ответ здесь , но он не работает.
  • Как показано выше, я попробовал источник событий, но не могу заставить его работать.
  • Я попытался обновить всю страницу, используя ngOnInit(), чтобы перезагрузить компонент nac bar, ноэто входит в бесконечный цикл (который, очевидно, является взломом, но почему бы и нет)

Есть ли способ, которым это может быть достигнуто чисто?

Ответы [ 2 ]

1 голос
/ 14 мая 2019

Решение для этого является основным, вы должны использовать наиболее распространенные функции 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, который вы можете получить от любого компонента, чтобы вы знали статус аутентификации и действовали на его основе, а с помощью наблюдаемых вы будетевсегда получать последний статус.

1 голос
/ 13 мая 2019

Вот как я это решил:

nav.component.html

...
<li *ngIf="!auth.isLoggedIn()">
  ...
</li>
<li *ngIf="auth.isLoggedIn()">
  ...
</li>
...

nav.component.ts

export class NavComponent implements OnInit {

  constructor(public auth: AuthService) {
  }
  ...

auth.service.ts

export class AuthService {
  ...
  public isLoggedIn() {
    return this.getId() !== null;
  }
  ...

В этом последнем методе this.getId () может быть для получения токена из localStorage.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...