I'm having difficulty with a component that doesn't refresh upon log in.
The component is navbar.component that contains links to my pages/components. On it, there is a "login" link that renders login.component on which I can provide username and password and click the login button. login.component uses user.service which calls the backend with the username and password provided by login.component, stores the received token and redirects to '/'.
At this point, the "login" link on navbar.component should be hidden and the "logout" link shown, but nothing happens until I click one of the other links on the navbar.
The 2 links are as follows:
<a [hidden]="userService.isLoggedIn" [routerLink]="['Login']">Login</a>
<a [hidden]="!userService.isLoggedIn" (click)="userService.logout();" href="javascript:void(0)">Logout</a>
I understand storing the token and redirecting from user.service doesn't trigger change detection, and that my property userService.isLoggedIn isn't an observable, so I know I'm missing something, but not sure what the approach is/should be.
I have tried injecting ApplicationRef to call the tick() method hoping this would trigger change detection, but failed.
Should I make my property userService.isLoggedIn an observable?
export class UserService {
isLoggedIn:BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
}
@Component({
...
template: `
<a [hidden]="userService.isLoggedIn | async" [routerLink]="['Login']">Login</a>
<a [hidden]="!(userService.isLoggedIn | async)" (click)="userService.logout();" href="javascript:void(0)">Logout</a>
`
})
export class MyComponent {
constructor(private userService: UserService) {}
}
I understand storing the token and redirecting from user.service doesn't trigger change detection
True, but if you are using the Angular Http service, when data is returned from the service, change detection should run after your callback function/method executes. Is your navbar component using the OnPush
change detection strategy? (If so, that would explain why is doesn't update.)
Günter's answer works because the async pipe not only automatically subscribe()
s to the observable, but it also calls markForCheck()
when a new value is emitted by the observable. This ensures that the component (and all of its ancestors) will be change detected, even if any of them are using OnPush
.
An alternative approach, which is a bit more imperative (rather than declarative), that still uses a BehaviorSubject, is as follows:
subscribe()
to the service observabledetectChanges()
. This will check the component and its children (if there are any). This could be more efficient than using markForCheck()
, especially if the NavBar doesn't have any children. But considering that logging in/out are likely rare events, efficiency probably doesn't matter. However, I prefer to use detectChanges()
when I'm only changing view/component state (as is the case here, based on how I implemented it below) rather than application state – i.e., something that affects multiple components, hence markForCheck()
would then be more appropriate.@Component({
...
template: `
<a [hidden]="userIsLoggedIn" [routerLink]="['Login']">Login</a>
<a [hidden]="!userILoggedIn" (click)="userService.logout()">Logout</a>
`
})
export class NavBar {
userIsLoggedIn = false;
constructor(private userService: UserService, private _ref: ChangeDetectorRef) {}
ngOnInit() {
this.userService.isLoggedIn.subscribe( userIsLoggedIn => {
this.userIsLoggedIn = userIsLoggedIn;
this._ref.detectChanges();
});
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With