简体   繁体   中英

Logged in user initial value is null Angular 7

I have a login component with a form and this function to perform the login

this.auth.login(this.f.email.value, this.f.password.value)
        .pipe(first())
        .subscribe(
            data => {
                this.router.navigate([this.returnUrl])
            },
            error => {
                this.serverError = error;
                this.loading = false;
            });

this.auth is my authentication service that looks like this

private currentUserSubject: BehaviorSubject<User>
public currentUser: Observable<User>

constructor(private http: HttpClient) {
    this.currentUserSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('currentUser')))
    this.currentUser = this.currentUserSubject.asObservable()
}

public get currentUserValue(): User {
    return this.currentUserSubject.value
}

login(email: string, password: string) {
    console.log('making login request')
    return this.http.post<any>(`${Config.apiUrl}/login`, { email, password })
        .pipe(map(user => {
            // login successful if there's a jwt token in the response
            if (user && user.token) {
                // store user details and jwt token in local storage to keep user logged in between page refreshes
                localStorage.setItem('currentUser', JSON.stringify(user))
                this.currentUserSubject.next(user)
            }

            return user
        }))
}

It makes a request to my node.js backend which then returns the user if the creds are correct. Then angular sets the value of currentUserSubject in AuthorizationService and localStorage to the user values that the server returns. This all is working how it's supposed to (I assume).

After my login component gets the data back from the authorization service it attempts to route the user to the auth.guard protected page. This is what it looks like it my routes.

{
    path: 'user',
    component: UserComponent,
    canActivate: [AuthGuard]
}

My AuthGuard looks like this

constructor(
    private router: Router,
    private authenticationService: AuthenticationService
) { }

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    const currentUser = this.authenticationService.currentUserValue

    if (currentUser) {
        // check if route is restricted by role
        if (route.data.roles && route.data.roles.indexOf(currentUser.role) === -1) {
            // role not authorised so redirect to home page

            this.router.navigate(['/'])
            return false
        }

        // authorised so return true
        return true
    }

    // not logged in so redirect to login page with the return url
    this.router.navigate(['/login'], { queryParams: { redirect: state.url } })
    return false
}

In AuthGuard canActivate() function currentUser is always null after the initial login request. So it redirects me back to my login page.

I can't figure out why it's null. The odd thing is, is that after I submit the login form and get redirected back to the login page, if I refresh the browser the currentUser value is not null anymore and the application behaves as I intend it to behave. Is there something I'm missing or doing that I shouldn't be doing that's causing thing?

Here's an image of the process and values that it goes through when I submit the login form 在此输入图像描述

The solution ended up being simpler than changing the BehaviorSubject to Subject. In my login component I had the AuthenticationService in the component providers like this.

@Component({
  selector: 'login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css'],
  providers: [AuthenticationService]
})

All I had to do was remove the service from the providers from all of my components that used it so it would end up looking like this.

@Component({
  selector: 'login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})

The thing that tipped me off was the solution to the problem at this link. https://github.com/angular/angular/issues/11618

In order two share a service between multiple components, you should provide the service at a level higher and not add the service to the providers of both components.

Registering any provider twice will create two independent instances.

So in order to use the AuthorizationService and maintain the state of the logged in user I had to remove it from the component providers.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM