Refresh Header after Login in Angular2

Pinski picture Pinski · Mar 23, 2017 · Viewed 31.4k times · Source

So I have a header component that displays either the User's name or "Sign In" depending on whether they are logged in or not. I also have a Login component that does all of the business logic of logging in. They currently do not have a parent / child relationship.

When the User logs in, the header does not refresh or change unless a full page refresh is done in the browser. I've been doing a lot of searching and reading online about different ways to do this. ngOnChanges, NgZone, ApplicationRef, and ChangeDetectorRef seem to be the most popular. I'm trying to implement this behavior in ChangeDetectorRef as this seems to be most relevant to my situation. However, I can't seem to find actual examples of how to use this.

I've coded it up but it does not seem to do anything. Any advice would be appreciated. I'd even accept that I'm taking the wrong approach and need to use another solution besides ChangeDetectorRef.

LoginComponent

import { Component, OnInit } from '@angular/core';
import { Response } from '@angular/http';
import { Router } from '@angular/router';

import { AuthenticationService } from '../service/authentication.service';

@Component({
    selector: 'login-component',
    templateUrl: './login.component.html'
})

export class LoginComponent implements OnInit {
    constructor(private router: Router, 
                private authenticationService: AuthenticationService) { }

    ngOnInit() {
        // Resets the login service.  
        // This is one of the events that should cause the refresh.
        this.authenticationService.logout();
    }

    login() {
        /*
        Authentication code
        This is the other event that should cause the refresh.
        */
    }
}

HeaderComponent

import { ChangeDetectorRef, ChangeDetectionStrategy, Component, OnInit } from '@angular/core';

import { Instance } from '../../entity/instance';

@Component({
    selector: 'header-component',
    templateUrl: './html/header.component.html',
    changeDetection: ChangeDetectionStrategy.OnPush
})

export class HeaderComponent {

    userName: string;

    constructor(private ref: ChangeDetectorRef) {
        this.ref.markForCheck();
    }

    ngOnInit(): void {
        var currentUser = JSON.parse(localStorage.getItem('currentUser'));

        this.userName = currentUser && currentUser.full_name;

        if (!this.userName) {
            this.userName = "User Name";
        }
    }
}

AppComponent

import { ChangeDetectorRef, ChangeDetectionStrategy, Component, OnInit } from '@angular/core';

import { Instance } from './entity/instance';
import { InstanceService } from './service/instance.service';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
    changeDetection: ChangeDetectionStrategy.OnPush
})

export class AppComponent implements OnInit {

    instances: Instance[];

    constructor(private instanceService: InstanceService) { }

    ngOnInit(): void {
    }
}

app.component.html

<header-component></header-component>

<router-outlet></router-outlet>

Answer

Pinski picture Pinski · Mar 27, 2017

So I ended up taking some of the advice of using my service to emit the change. I read in some places on Stack Overflow that using a service this way was a bad pattern, that emits should only happen from a child component to a parent component. So I'm not sure this is the "proper" way, but it works for me because I want to have multiple components know of this event.

I already had a service that was dealing with my authentication, so all I had to do was give it an emitter, emit at the proper times, and then listen for the emit in my component.

Header Component

export class HeaderComponent {
    userName: string;

    constructor(private authenticationService: AuthenticationService) {
        authenticationService.getLoggedInName.subscribe(name => this.changeName(name));
    }

    private changeName(name: string): void {
        this.userName = name;
    }
}

Authentication Service

@Injectable()
export class AuthenticationService {
    @Output() getLoggedInName: EventEmitter<any> = new EventEmitter();

    login(email: string, password: string): Observable<boolean> {
        if (successfulLogIn(email, password)) {
            this.getLoggedInName.emit(fullName);
            return true;
        } else {
            this.getLoggedInName.emit('Sign In');
            return false;
        }
    }

    logout(): void {
        this.getLoggedInName.emit('Sign In');
    }
}