2017-10-11 77 views
1

我有2個Angular2組件,我希望能夠共享一個值。兩個Angular2組件之間的通信問題

我的應用組件的代碼是:其在<router-outlet></router-outlet>加載

<app-header></app-header> 

<router-outlet></router-outlet> 

<app-footer></app-footer> 

我的登錄組件打字稿代碼是:

import { Component, OnInit } from '@angular/core'; 
import { MatInput } from '@angular/material'; 
import { Router } from '@angular/router'; 

import { LoginService } from '../../services/login.service'; 
import { User } from '../../models/user'; 

@Component({ 
    selector: 'app-login', 
    templateUrl: './login.component.html', 
    providers: [ LoginService ], 
    styleUrls: ['./login.component.css'] 
}) 
export class LoginComponent implements OnInit { 
    public user = new User('', '', new Array<string>()); 
    public errorMsg = ''; 
    public isLoading = false; 

    constructor(
    private loginService: LoginService, 
    private router: Router 
) { } 

    ngOnInit() { 
    if (this.loginService.getCurrentUser() !== null) { 
     this.router.navigate(['home']); 
    } 
    } 

    login() { 
    this.isLoading = true; 
    const obs = this.loginService.login(this.user); 
    obs.subscribe(
     res => { 
     if (res !== true) { 
      this.errorMsg = 'Incorrect Username/Password'; 
      this.loginService.loginStatusChange(false); 
     } else { 
      this.loginService.loginStatusChange(true); 
     } 
     }, 
     err => { 
     this.isLoading = false; 
     this.errorMsg = err._body; 
     this.loginService.loginStatusChange(false); 
     }, 
    () => { 
     this.isLoading = false; 
     } 
    ); 
    obs.connect(); 
    } 
} 

我的標題組件打字稿是:

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

import { User } from '../../models/user'; 

import { LoginService } from '../../services/login.service'; 

@Component({ 
    selector: 'app-header', 
    templateUrl: './header.component.html', 
    providers: [ LoginService ], 
    styleUrls: ['./header.component.css'] 
}) 
export class HeaderComponent implements OnInit { 
    public currentUser: string; 

    constructor(private loginService: LoginService) { } 

    ngOnInit() { 
    const currentUser = this.loginService.getCurrentUser(); 
    if (currentUser !== null) { 
     this.currentUser = currentUser.username; 
    } 
    this.loginService.loginObservable 
        .map((status) => { 
        if (status) { 
         return this.loginService.getCurrentUser(); 
        } 
        return null; 
        } 
       ) 
       .subscribe((user) => { 
        const thisUser = this.loginService.getCurrentUser(); 
        if (thisUser !== null) { 
        this.currentUser = thisUser.username; 
        } 
       }); 
    } 

    logout() { 
    this.loginService.logout(); 
    this.loginService.loginStatusChange(false); 
    } 
} 

而最後我的標題組件視圖是:

<div id="wrapper"> 
    <section> 
     <div id="topHeader"> 
      <div class="oLogo"> 
       <img id="OLogoImg" src="../../assets/images/Luceco-O-Logo-Transparent.png" alt="o-logo" height="20" /> 
      </div> 
     </div> 
    </section> 
</div> 
<div class="container body-content"> 
    <div id="header"> 
     <div class="pageWrap"> 
      <a id="logo" > 
       <img id="logoImg" src="../../assets/images/Luceco-Logo-Transparent.png" alt="logo" height="28" /> 
      </a> 
      <ul id="menu"> 
       <li id="home-menu" class="top-level home-menu"> 
       <a href="#">Home</a> 
       </li> 

<--FOLLOWING COMPONENT NEEDS TO BE DISPLAYED AFTER LOGIN --> 

       <li *ngIf="currentUser != null" id="logout-menu" class="top-level logout-menu"> 
       <a href="#" (click)="logout()">Log Out</a> 
       </li> 
      </ul> 
     </div> 
    </div> 

login服務:

import { Injectable } from '@angular/core'; 
import { Http, Headers, RequestOptions, Response } from '@angular/http'; 
import { Router } from '@angular/router'; 

import 'rxjs/rx'; 
import { ConnectableObservable } from 'rxjs/rx'; 
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; 
import { Observable } from 'rxjs/Observable'; 

import { User } from '../models/user'; 

@Injectable() 
export class LoginService { 
    private authApiUrl = 'http://192.168.1.201/ForkliftHelperAPI/api/Auth'; 

    private loginBehaviourSubject = new BehaviorSubject<boolean>(false); 
    public loginObservable = this.loginBehaviourSubject.asObservable(); 

    constructor(private router: Router, 
       private _http: Http) { } 

    loginStatusChange(isLoggedIn: boolean) { 
    this.loginBehaviourSubject.next(isLoggedIn); 
    } 

    login(user: User): ConnectableObservable<any> { 
    let result: User; 
    const body = JSON.stringify(user); 
    const headers = new Headers({ 
     'Content-Type': 'application/json' 
    }); 
    const options = new RequestOptions({ 
     headers: headers 
    }); 
    const obsResponse = this._http.post(this.authApiUrl, body, options) 
           .map(res => res.json()) 
           .publish(); 

    obsResponse.subscribe(
       (res: User) => { 
        result = res; 
        if (result) { 
        user.securityGroups = result.securityGroups; 
        sessionStorage.setItem('user', JSON.stringify(user)); 
        this.router.navigate(['home']); 
        } 
       }, 
       err => console.log(err) 
    ); 
    return obsResponse; 
    } 

    logout() { 
    sessionStorage.removeItem('user'); 
    this.router.navigate(['login']); 
    } 

    getCurrentUser() { 
    const storedUser = JSON.parse(sessionStorage.getItem('user')); 
    if (!storedUser) { 
     return null; 
    } 
    return new User(storedUser.username, storedUser.password, storedUser.securityGroups); 
    } 

    isLoggedIn() { 
    if (this.getCurrentUser() === null) { 
     this.router.navigate(['login']); 
    } 
    } 
} 

所以基本上我的問題是,當LoginComponent的登錄方法完成,我希望它的HeaderComponent內設置currentUser變量,以便在產生下頁通過路由器插座,標題正確顯示註銷按鈕。如果在重定向之後手動刷新頁面,則更新會正確發生,但是,在重定向時不會刷新標題,而只會刷新router-outlet的內容。

我已經嘗試使用服務以及使用@Input()和@Output()但沒有運氣,也許我一直在錯誤地使用它們。

我的主要問題似乎是,當重定向和導航發生時,頁眉和頁腳組件不會刷新,因爲它只是<router-outlet></router-outlet>中受影響的組件。我這樣做是爲了防止在每個其他組件中都有頁眉和頁腳組件,但如果這是實現我所需的唯一方法,那麼就這樣做吧。

任何幫助將不勝感激。

+0

將不勝感激,如果你可以做一個再現,看你做了什麼的 – amal

+1

可能的複製[Angular2 - 使用服務的組件之間共享數據(https://stackoverflow.com/questions/35273106/angular2-share-data-between-components-using-services) –

+1

最有可能是dup –

回答

0

首先,將您的LoginService提供給您的根模塊,而不是在您的HeaderLogin組件中提供它。

 @NgModule({ 
     // other things 
     providers: [LoginService,..........], 
     bootstrap: [AppComponent] 
    }) 
    export class AppModule { } 

您對使用EventEmiiter或Rxjs BehaviorSubject爲這種成分對組件通信。

通常,在一個組件中更改任何值不會觸發其他組件中的更改,除非您明確告知angular來這麼做。 有幾個機制可以做到這一點。

最好的方法是使用RxJs SubjectBehaviorSubject來達到此目的。

您可以創建一個在您loginService和過程的BehaviorSubject如下:

login服務類:

import 'rxjs/add/operator/do'; 
    import 'rxjs/add/operator/share'; 

    export class LoginService { 

     private loginbehaviorSubject = new BehaviorSubject<boolean>(true); 
     public loginObservable$ = this.loginbehaviorSubject.asObservable(); 

     loginStatusChange(isLoggedIn: boolean){ 
     this.loginbehaviorSubject.next(isLoggedIn); 
     } 


     login(user: User): ConnectableObservable<any> { 
     let result: User; 
     const body = JSON.stringify(user); 
     const headers = new Headers({ 
      'Content-Type': 'application/json' 
     }); 
     const options = new RequestOptions({ 
      headers: headers 
     }); 
     return this._http.post(this.authApiUrl, body, options) 
            .map(res => res.json()) 
            .do((res: User) => { 
              result = res; 
              if (result) { 
              user.securityGroups = result.securityGroups; 
              sessionStorage.setItem('user', JSON.stringify(user)); 
              } 
             }, 
             err => console.log(err) 
            ) 
            .share(); 
     } 
     removeUserFromSession(){ 
      if(sessionStorage.getItem('user')){ 
       sessionStorage.removeItem('user'); 
      }  
     } 
     logout() { 
     this.removeUserFromSession(); 
     this.router.navigate(['login']); 
     } 

    } 

LoginComponent

ngOnInit() { 
    if (this.loginService.getCurrentUser() !== null) { 
     this.router.navigate(['home']); 
    } 
    } 

    login() { 
    this.isLoading = true; 
    const obs = this.loginService.login(this.user); 
    obs.subscribe(
     res => { 
     this.loginService.loginStatusChange(true); 
     if (res !== true) { 
      this.errorMsg = 'Incorrect Username/Password'; 
     } else { 
      this.router.navigate(['home']); 
     } 
     }, 
     err => { 
     this.isLoading = false; 
     this.errorMsg = err._body; 
     this.loginService.loginStatusChange(true); 
     }, 
    () => { 
     this.isLoading = false; 
     } 
    ); 
} 

在HeaderComponent:

export class HeaderComponent implements OnInit { 
    public currentUser: string; 

    constructor(private loginService: LoginService) { } 

    ngOnInit() { 
    const currentUser = this.loginService.getCurrentUser(); 
    if (currentUser !== null) { 
     this.currentUser = currentUser.username; 

    } 

    this.loginService.loginObservable$ 
    .subscribe((isStatusChanged) => { 
     const currentUser = this.loginService.getCurrentUser(); 
     this.currentUser = currentUser.username; 
    }); 
    } 

    logout() { 
    this.loginService.logout(); 
    this.loginService.loginStatusChange(true); // notice this line 
    } 
} 
+0

不,因爲頭已經初始化,'ngOnInit'不會再次觸發。這不起作用。但是,我同意只提供一次服務的部分。 – Faisal

+0

你不應該倒下你的每一個答案。如果你不喜歡任何答案,你可以發表評論。你知道這是完全令人沮喪的。你應該溝通。這就是你將如何得到正確的答案。 – asmmahmud

+0

我已經低估了,因爲這個答案不能解決問題。不要故意阻止:)所有的upvoted,現在至少正確的答案下面的這種方法:https://stackoverflow.com/a/46049546/1791913 – Faisal

1

您應該使用EventBus,創建一個單例服務,它必須是您的主模塊內部的提供者,並且不要將其作爲提供者放在其他位置。

的理念是:

login.component.ts

constructor(public eventBus: EventBus) {} 

onLoginSuccess(currentUser: any): void { 
    this.eventBus.onLoginSuccess.next(currentUser); 
} 

header.component.ts

constructor(public eventBus: EventBus) { 
    this.eventBus.onLoginSuccess.subscribe((currentUser: any) => this.currentUser = currentUser); 
} 

eventbus.service.ts

@Injectable() 
export class EventBus { 
    onLoginSuccess: Subject<any> = new Subject(); 
} 

當然你必須處理這些潛艇criptions和其他一切,這只是一個方法。

當您的用戶完成登錄後,eventBus將會通過onLoginSuccess事件觸發header component

+0

這似乎是一個好方法,但我努力讓它工作,你能擴大你的答案,以顯示正確的方式訂閱等? – DaRoGa

+0

我似乎無法使HeaderComponent檢測到更改並相應地調整顯示 – DaRoGa

0

創建LoginService的單例實例。

@Injectable() 
export class LoginService { 
    static instance: LoginService; 
    constructor() { 
    return LoginService.instance = LoginService.instance || this; 
    } 
} 

將此單例服務包括到route-oulet組件和標頭組件中。 在頭文件組件中,您可以使用ngOnChanges或ngDoCheck方法來觀察登錄服務中的變量,並且一旦其值被更改,函數中的代碼將執行而不刷新。

ngOnChanges(changes: SimpleChanges) {}

ngDoCheck() {}