Search code examples
angularangular2-components

Component communication using service/broadcasting : Angular2


Hi all I am beginner in angular2. I am creating a small demo application for learning purpose.

app.component.ts

template: `
    <header-content></header-content>
    <main-content></main-content>
    <footer-content></footer-content>
  `

In my header I have a navigation section like home, login etc.

Main-content-template structure

<div class="container main">
        <div class="row">
          <router-outlet></router-outlet>
        </div>
 </div> 

As you can see I have placed the router-outlet for the header and footer section links inside main-content.

enter image description here

By defalut I am at Login page /login.

I have a separate component for every links.

What I want is when user clicks on submit button on login page I am redirecting him to some other xyz page.But along with that I want my Login link to get hide from the header nav and display a user name.

   <li *ngIf='!isUserLoggedIn'><a routerLink="/login">Login</a></li>
   <li *ngIf='isUserLoggedIn'><a>Welcome {{user}}</a></li>

HeaderComponent.ts

@Component({
  moduleId : module.id,
  selector : 'header-content',
  templateUrl: 'Header.html',
  providers: [LoginService]
})

export class HeaderComponent implements OnInit {
  isUserLoggedIn = false;
  user : string;

  constructor(private loginService: LoginService) { }

  getUserName() :void {
      this.user = this.loginService.getUserName(); // reading data from localstorage on page refresh
      if(this.user) {
        this.isUserLoggedIn = true;
      }
  }

  ngOnInit(): void {
     this.getUserName();
     // Will fire everytime other component use the setter this.loginService.setLogged()
     this.loginService.getLogged().subscribe((logged) => {
         console.log('Welcome===>', logged);
     });
  }
}

Login-Service.ts

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';

@Injectable()

export class LoginService {
    username : string;

        private logged: boolean;
        private subject: Subject<boolean> = new Subject<boolean>();

        setLogged(logged: boolean): void {
            this.logged = logged;
            this.subject.next(logged);
        }

        getLogged() {
            return this.subject.asObservable();
        }


    constructor() {
        if(localStorage.getItem('userName')){
            this.username = localStorage.getItem('userName');
        }
    }

    getUserName():string {
        if(this.username) 
         return this.username;
    }
}

I have read a lot of other question and answers blog on stackoverflow , and I tried to use observable as you can see in the code. But when I click on submit button , the line of code inside ngOnInit() ( this.loginService.getLogged().subscribe((logged) => { ) is not getting called in order to update the value of isUserLoggedIn .

How can I broadcast or trigger some mechanism and what are the ways and the best way of doing it.

I don't know what mistake I am doing.

My other question is how can the methods in header-content can communicate with its siblings main-content or footer-content and also with the parent my-app component section.As in many questions answer is there for parent child components but not for the siblings.

Any help is appreciated !!!

Thanks


Solution

  • In your login-service you call the method that returns the username. That means that there you have a promise.

    So back in your headerComponent change the get function to this:

    getUserName() :void {
          this.user = this.loginService
                          .getUserName()
                          .success(data => {
                                 this.isUserLoggedIn = true;
                          }
      }
    

    I believe this will fix your problem based on what I see here. I would suggest to create a working snippet though ;)

    UPDATE Based on your comments I suggest you to use BehaviorSubject which makes life easy to broadcast from service to component anything you want (from booleans to objects etc).

    Here's an example based in your case:

    In your login service (I'm attaching only the code that will make this happen):

    import { BehaviorSubject } from 'rxjs/BehaviorSubject';
    import { Injectable } from '@angular/core';
    
    @Injectable()
    
    export class LoginService {
    
        userUpdated: any;
    
        constructor() {
            this.userUpdated = new BehaviorSubject<any>(false);
        }
    
        getUserName() :void {
            this.user = this.loginService
                            .getUserName()
                            .success(data => {
                                this.userUpdated.next(true);
                                return data;
                            }
        }
    
    }
    

    Next you go to you headerComponent code and edit it like this to subscribe to this:

    @Component({
      moduleId : module.id,
      selector : 'header-content',
      templateUrl: 'Header.html',
      providers: [LoginService]
    })
    
    export class HeaderComponent implements OnInit, OnDestroy {
      isUserLoggedIn = false;
      user : string;
      sub: any;
    
      constructor(private loginService: LoginService) { }
    
      ngOnInit(): void {
        this.sub = this.loginService.userUpdated.subscribe(
          data => {
            if(data.username) {
              this.isUserLoggedIn = true;
          }
        );
    
      }
    
      ngOnDestroy() {
        this.sub.unsubscribe();
      }
    }