两个Angular2组件之间的通信问题

时间:2017-10-11 15:43:41

标签: angular typescript angular2-routing angular-components

我有2个Angular2组件,我希望能够共享一个值。

我的应用组件代码是:

<app-header></app-header>

<router-outlet></router-outlet>

<app-footer></app-footer>

我在<router-outlet></router-outlet>中加载的登录组件打字稿代码是:

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变量,这样当路由器插座生成下一页时,标题会显示注销按钮正确。如果您在重定向后手动刷新页面,则更新正确,但是重定向时不刷新标头,只有路由器插座的内容。

我尝试过使用服务以及使用@Input()和@Output(),但没有运气,也许我一直在使用它们。

我的主要问题似乎是当重定向和导航发生时,页眉和页脚组件不会刷新,因为它只是<router-outlet></router-outlet>中受影响的组件。我是这样做的,以防止必须在每个其他组件中都有页眉和页脚组件,但如果这是实现我需要的唯一方法那么就是它。

非常感谢任何帮助。

3 个答案:

答案 0 :(得分: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();
}

当然,您必须处理订阅和其他所有内容,这只是一个操作方法。

当您的用户完成登录后,eventBus会在header component事件中点击onLoginSuccess

答案 1 :(得分:0)

首先,将您的LoginService提供给您的根模块,而不是在HeaderLogin组件中提供。{/ p>

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

您必须使用EventEmiiter或Rxjs BehaviorSubject来进行此类组件与组件之间的通信。

通常,更改一个组件中的任何值都不会触发其他组件的更改,除非您明确告知angular这样做。 有几种机制可以做到这一点。

最好的方法是为此目的使用RxJ SubjectBehaviorSubject

您可以在BehaviorSubject中创建loginService,操作步骤如下:

LoginService类:

   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
  }
}

答案 2 :(得分:0)

创建LoginService的单例实例。

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

将此单例服务包含在route-oulet组件和标头Component中。 在header组件中,您可以使用ngOnChanges或ngDoCheck方法在登录服务中查看变量,一旦其值发生更改,函数内的代码将在不刷新的情况下执行。

ngOnChanges(changes: SimpleChanges) {}

ngDoCheck() {}