在Angular2中登录后刷新标题

时间:2017-03-23 21:25:25

标签: angular typescript angular2-changedetection

所以我有一个标题组件,显示用户的名字或"登录"取决于他们是否登录。我还有一个Login组件,它执行登录的所有业务逻辑。它们目前没有父/子关系。

当用户登录时,除非在浏览器中完成整页刷新,否则标题不会刷新或更改。我在网上搜索和阅读了很多不同的方法。 ngOnChanges,NgZone,ApplicationRef和ChangeDetectorRef似乎是最受欢迎的。我试图在ChangeDetectorRef中实现此行为,因为这似乎与我的情况最相关。但是,我似乎无法找到如何使用它的实际示例。

我已将其编码但似乎没有做任何事情。任何意见,将不胜感激。我甚至接受我采取了错误的方法,除了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>

6 个答案:

答案 0 :(得分:14)

所以我最终采取了一些使用我的服务来发出变化的建议。我在Stack Overflow上的某些地方读到,使用这种方式的服务是一种糟糕的模式,只能从子组件发送到父组件。所以我不确定这是&#34;正确的&#34;方式,但它适用于我,因为我想知道这个事件的多个组件。

我已经有了一个处理我的身份验证的服务,所以我所要做的就是给它一个发射器,在适当的时间发出,然后在我的组件中侦听发射。

标题组件

export class HeaderComponent {
    userName: string;

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

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

身份验证服务

@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');
    }
}

答案 1 :(得分:7)

@Pinski很好。但这可能更容易。这是一种发出和订阅数据的替代方法。

标题组件

export class HeaderComponent implements OnInit {
    userName: string;

    constructor(private authenticationService: AuthenticationService) {}

   NgOnInit() {
      this.authenticationService.getLoggedInName.subscribe(name => this.userName = name);
   }

}

身份验证服务

@Injectable()
export class AuthenticationService {
    public getLoggedInName = new Subject(); //Alternate method to Emitting data across Components. Subject() is doing both Emitting data and Subscribing it in another component. So its the best way to compare with Emitting using Output.

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

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

尝试Subject()。快乐的编码。

答案 2 :(得分:4)

你可以这样做 - &gt;

标题组件 - &gt;

 ngOnInit() {
this.subscription = this.emitterService.getEmitter('userDetails').subscribe((user: Object) => {
            if(user)
                this.userName = user["name"];
        });
    }
ngOnDestroy() {
        // prevent memory leak when component is destroyed
        this.subscription.unsubscribe();
    }

登录服务 - &gt;

this.emitterService.getEmitter('userDetails').emit(userDetails);

您从登录组件发出的值将被捕获到您的菜单组件中。

答案 3 :(得分:1)

Session.ts

export class Session {


isLoggedIn(): boolean{
    return localStorage.getItem('username') == null ? false : true;
}
setLoggedInUser(username: string, icon: string): void {
    localStorage.setItem('username', username);
    localStorage.setItem('icon', icon);
}
logout(){
    localStorage.clear();
}

}

标题组件

    import { Component, OnInit, Input } from '@angular/core';
import { Session } from '../Session';
@Component({
  selector: 'AppHeader',
  templateUrl: './app-header.component.html',
  styleUrls: ['./app-header.component.scss']
})
export class AppHeaderComponent implements OnInit {
  @Input()
  session: Session;
  constructor() { }

  ngOnInit(): void {
    this.session = new Session();
  }
  logout(){
    this.session.logout();    
  }
}

HTML

<header>
<mat-toolbar>        
    <span>Example</span>
    <span class="example-spacer"></span>
    <button *ngIf="session.isLoggedIn()" routerLink="quests" mat-icon-button class="example-icon favorite-icon" aria-label="Example icon-button with heart icon">
        <mat-icon>error</mat-icon>
    </button>
    <button *ngIf="session.isLoggedIn()" routerLink="stats" mat-icon-button class="example-icon favorite-icon" aria-label="Example icon-button with heart icon">
        <mat-icon>assessment</mat-icon>
    </button>
    <button *ngIf="session.isLoggedIn()" routerLink="equipment" mat-icon-button class="example-icon favorite-icon" aria-label="Example icon-button with heart icon">
        <mat-icon>accessibility</mat-icon>
    </button>
    
    <button *ngIf="!session.isLoggedIn()" routerLink="login" mat-icon-button class="example-icon favorite-icon" aria-label="Example icon-button with heart icon">
        <mat-icon>login</mat-icon>
    </button>
    <button *ngIf="!session.isLoggedIn()" routerLink="register" mat-icon-button class="example-icon" aria-label="Example icon-button with share icon">
        <mat-icon>add</mat-icon>
    </button>        
    <button *ngIf="session.isLoggedIn()" routerLink="login" (click)="logout()" mat-icon-button class="example-icon favorite-icon" aria-label="Example icon-button with heart icon">
        <mat-icon>power_settings_new</mat-icon>
    </button>
</mat-toolbar>

答案 4 :(得分:0)

我来晚了,但是我也在尝试登录后更新我的标头组件,我已经按照您的方法进行操作,但是它没有更新我的标头组件。

user.service.ts

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

@Injectable()
export class UserService {
constructor(public router: Router) {}
public getLoggedInName = new Subject(); 

async storeData(data) {
    this.getLoggedInName.next(data);
    localStorage.setItem('userData', JSON.stringify(data));
    return this.router.navigate(['']); // after login we navigate to home component, but I want to display the email in header component
}

getData() {
   return JSON.parse(localStorage.getItem('userData'));
}

logOut() {
   localStorage.setItem('userData', '');
   localStorage.clear();
   return this.router.navigate(['']);
}
}

login.component.ts

public socialSignIn(socialPlatform: string) {
      let socialPlatformProvider;
      if (socialPlatform === 'facebook') {
         socialPlatformProvider = FacebookLoginProvider.PROVIDER_ID;
      } else if (socialPlatform === 'google') {
         socialPlatformProvider = GoogleLoginProvider.PROVIDER_ID;
      }

      this.socialAuthService.signIn(socialPlatformProvider).then(userData => {
         this.apiConnection(userData);
      });
  }

  apiConnection(data){
      this.userPostData.email = data.email;
      this.userPostData.name = data.name;
      this.userPostData.provider = data.provider;
      this.userPostData.provider_id = data.id;
      this.userPostData.provider_pic = data.image;
      this.userPostData.token = data.token;
      this.user.storeData(data);

  }

socialSignIn()方法,我们在Facebook图标上单击。

nav-menu.component.ts 是我的标头组件,希望在其中我的userData变量具有登录的用户详细信息。

import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';

@Component({
  selector: 'app-nav-menu',
  templateUrl: './nav-menu.component.html',
  styleUrls: ['./nav-menu.component.css']
})
export class NavMenuComponent implements OnInit {
  isExpanded = false;
  isLoggedIn = false;
  public userData: any;

  constructor(public user: UserService){
    this.setUserSessionData();
  }

  ngOnInit() {
    this.user.getLoggedInName.subscribe(name => this.userData = name);
 }      

  public logOut(){
    debugger;
    this.user.logOut();
  }

  public setUserSessionData(){
    this.userData = this.user.getData();
    this.isLoggedIn = !this.userData;
  }
}

我的 nav-menu.component.html ,我将在其中显示已登录的用户电子邮件。

 <button class="nav-text" clrDropdownTrigger>
                {{userData?.email}}
                <clr-icon shape="caret down"></clr-icon>
 </button>

答案 5 :(得分:0)

我正面临类似的问题。密码重置后,当我尝试登录时,所有API调用均失败,因为直到刷新整页后才在调用中发送标头。

对我来说,当我尝试在重置密码后立即登录时,cookie被存储在不同的路径中。 通过设置cookie时指定路径来修复它。

类似的东西:

this.cookies.set(JWT_TOKEN,Token,0.33,'/');