【问题标题】:Refresh a particular component in Angular刷新 Angular 中的特定组件
【发布时间】:2019-09-30 18:20:05
【问题描述】:

Angular 新手,所以听起来可能是一个微不足道的问题,但是到目前为止,SO 上提供的所有解决方案都未能奏效。 我有一个简单的登录组件,其中在提交时,我将用户重定向到个人资料页面。我可以将用户发送到上述组件,但顶部的导航栏不会自动刷新,即我保留了会话检查,所以当用户登录时,导航栏应该自动显示 Logout 按钮而不是 @ 987654323@ 按钮。我的代码文件是这样的:

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']));
    }
  });
});
}

问题
当我手动刷新页面时,导航栏会根据书面逻辑反映更改,效果很好。但是,我希望在用户实际登录并且不需要手动刷新页面时发生这种情况。

我的尝试

  • 我尝试使用答案here,但它不起作用。
  • 如上所示,我尝试了一个事件发射器,但我无法让它工作。
  • 我尝试使用ngOnInit() 刷新整个页面以重新加载 nac bar 组件,但它进入无限循环(即 显然是黑客;但为什么不)

有没有办法可以干净利落地实现?

【问题讨论】:

  • 我们能看到您在哪里订阅 refreshEvent 吗?
  • @dota2pro 你为什么要那样做,这是一个组件,不需要比角度基础更复杂的东西。利用内置指令和数据绑定可以轻松实现我认为用户想要的。
  • @Nanotron 我只想知道 this.refreshEvent.emit();正在订阅或用于什么目的

标签: javascript angular typescript angular-components


【解决方案1】:

解决方案是基本的,您应该使用 Angular 最常见的功能。我将引导您完成思考过程,然后向您展示一些示例代码。

思考过程:

问题:我们需要随时了解用户是否登录。
解决方案:我们将提供一项服务,告诉我们是否用户是否登录

问题:导航栏要依赖用户的认证状态
解决方法:我们会使用认证服务返回的状态,有条件地显示一组基于用户的身份验证状态的项目或另一组项目

代码级问题:
您的代码中存在某些问题,这将使您难以进一步开发依赖于身份验证状态的其他功能。

我已经写了两个步骤来改进你的代码,第一个只是改进你的数据流和代码质量。第二步是用更动态的数据流完成更正后的代码。


第 1 步

服务
我们将在 Authentication Service 中有一个变量,告诉我们用户是否已经登录:

private isUserLoggedIn: boolean = false;

我们需要将所有身份验证逻辑移到身份验证服务中。由于我没有this.auth.loginUser(formData) 的代码,我将自己从新的身份验证服务中调用它,但请注意,该函数的代码应该在我们的新 login 函数中。 此外,没有必要将登录的 HTTP 调用保持为可观察的,因为您只会得到一个答案,因此我们可以使用 .toPromise() 将其转换为 Promise。

调用 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 来检查用户是否登录(以防我们希望用户在每次刷新后不必登录):
双重否定 !! 告诉我们值是真还是假,所以如果我们在 localStorage 中的 loggedUser 键上有什么东西,我们会在用户登录时接受它

// 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 将数据实时传递给可观察对象的订阅者。更多关于这些,here

现在,每当我们更新登录状态时,我们都会调用以下内容:

this.subject.next(this.isUserLoggedIn);

这样,所有订阅者都将收到此更改的通知。

我们需要一个函数来返回组件可以订阅的 observable:

public isLoggedInObservable(): Observable<boolean> {
        return this.observable;
}

剩下的就是从需要实时更新身份验证状态的组件中订阅这个 observable,在我们的例子中是导航组件(在 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);
                });
        })
    }
}

这就是最终的导航组件的样子:

@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,以便您了解身份验证的状态并根据它采取行动,并且使用 observables,您将随时获取最新状态。

【讨论】:

    【解决方案2】:

    我就是这样解决的:

    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 获取令牌。

    【讨论】:

    • 干净,简单,按书本。
    猜你喜欢
    • 2018-08-03
    • 1970-01-01
    • 2018-05-28
    • 2017-12-29
    • 1970-01-01
    • 2017-03-05
    • 1970-01-01
    • 2022-01-02
    • 2019-07-17
    相关资源
    最近更新 更多