登录后导航栏绑定未更新

时间:2016-08-10 19:41:47

标签: angular

我对angular2有点新,我理解所有的基础知识,但我有一个关于通过我不理解的服务绑定组件的问题。我真的很想知道在这种情况下我做错了什么,而不仅仅是有一个需要改变的基本答案。

我有一个app.component.ts来处理我的应用程序路由并触发我的登录组件

# app.component.ts
import {Component, OnInit} from "@angular/core";
import {LoginComponent} from "./authentication/login.component";
import {ROUTER_DIRECTIVES} from '@angular/router';
import {NavbarComponent} from "./navbar/navbar.component";
import {User} from "./authentication/user.model";
import {AuthService} from "./authentication/auth.service";

@Component({
    moduleId: module.id,
    selector: "app",
    templateUrl: "./app.component.html",
    directives: [
        ROUTER_DIRECTIVES,
        NavbarComponent,
        LoginComponent
    ],
    precompile: [
        AppComponent,
        NavbarComponent,
        LoginComponent
    ]
})
export class AppComponent implements OnInit {

    public user:User;

    constructor(private authService:AuthService) {

    }

    ngOnInit() {
        this.user = this.authService.getUser();
        console.log("Application initialized ...");
    }
}

由于这个类用于主路由并且登录是强制性的,因此我认为将用户对象放在那里作为公共属性是明智的,这样我就可以轻松地从任何地方到达它。登录(通过auth.guard转发)由以下Component和auth服务处理:

# login.component.ts
import {Component, OnInit} from '@angular/core';
import {Router, ROUTER_DIRECTIVES} from '@angular/router';
import {NgForm} from '@angular/forms';
import {User} from "./user.model";
import {AuthService} from "./auth.service";

@Component({
    moduleId: module.id,
    selector: 'authentication',
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.css'],
    directives: [ROUTER_DIRECTIVES]
})
export class LoginComponent implements OnInit {

    constructor(public router:Router, private authService:AuthService) {
    }

    ngOnInit() {
    }

    login(event, username, password) {
        this.authService.login(username, password);
        this.router.navigate(['']);
    }
}

服务:

# auth.service.ts
import {Injectable} from '@angular/core';
import {User} from "./user.model";

@Injectable()
export class AuthService {

    public user:User;

    login(username:string, password:string) {
        this.user = new User();
        this.user.id = 1;
        this.user.username = 'dummy username';
        this.user.email = 'foo@bar.com';
        this.user.name = 'My dummy testname';
        this.user.token = 'randomtokenverherefromwebserviceandstuff';
        localStorage.setItem('user', JSON.stringify(this.user));
    }

    logout() {
        localStorage.removeItem('user');
    }

    getUser() {
        this.user = JSON.parse(localStorage.getItem('user'));
        return this.user;
    }
}

所有这一切都适合我至少的感觉。登录被很好地触发,登录后用户被重定向到在路由器插座中解析的通用组件,这很好。 app.component.html中提供了此模板,如下所示:

# app.component.html
<app-navbar [user]="user"></app-navbar>

<div class="container-fluid">
    <router-outlet></router-outlet>
</div>

这里的问题是登录后导航栏用户为空并且根本不显示。然而,一旦页面刷新,导航栏用户就可以从本地存储中获取用户。因此我猜它与绑定有关,它不会令人耳目一新,因为它会检索它onInit我认为?以下是导航栏本身的代码:

# navbar.component.ts
import {Component, Input} from "@angular/core";
import {User} from "../authentication/user.model";
import {ROUTER_DIRECTIVES, Router} from '@angular/router';
import {AuthService} from "../authentication/auth.service";

@Component({
    moduleId: module.id,
    selector: "app-navbar",
    templateUrl: "./navbar.component.html",
    directives: [ROUTER_DIRECTIVES]
})
export class NavbarComponent {

    @Input() user:User;

    constructor(public router:Router, private authService:AuthService) {
    }

    logout() {
        this.authService.logout();
        this.router.navigate(['login']);
    }
}

我希望有人可以告诉我我做错了什么,或者甚至告诉我,我正在做的事情是正确的。

提前致谢

2 个答案:

答案 0 :(得分:0)

尝试添加此修改

providers:[AuthService],

修改

这里也是Auth0配置https://github.com/auth0-samples/auth0-angularjs2-systemjs-sample

的git

答案 1 :(得分:-1)

我已使用bi-directional communication from angular documentation。我在下面的主题中添加了文字。

家长和孩子通过服务进行沟通

父组件及其子组件共享一个服务,其接口支持在系列内进行双向通信。

服务实例的范围是父组件及其子组件。此组件子树外的组件无法访问服务或其通信。

MissionServiceMissionControlComponent与多个AstronautComponent子项相关联。

import { Injectable } from '@angular/core';
import { Subject }    from 'rxjs/Subject';
@Injectable()
export class MissionService {
  // Observable string sources
  private missionAnnouncedSource = new Subject<string>();
  private missionConfirmedSource = new Subject<string>();

  // Observable string streams
  missionAnnounced$ = this.missionAnnouncedSource.asObservable();
  missionConfirmed$ = this.missionConfirmedSource.asObservable();

  // Service message commands
  announceMission(mission: string) {
    this.missionAnnouncedSource.next(mission);
  }
  confirmMission(astronaut: string) {
    this.missionConfirmedSource.next(astronaut);
  }
}

MissionControlComponent都提供与其子代共享的服务实例(通过providers元数据数组),并通过其构造函数将该实例注入其自身:

import { Component }          from '@angular/core';
import { MissionService }     from './mission.service';
@Component({
  selector: 'mission-control',
  template: `
    <h2>Mission Control</h2>
    <button (click)="announce()">Announce mission</button>
    <my-astronaut *ngFor="let astronaut of astronauts"
      [astronaut]="astronaut">
    </my-astronaut>
    <h3>History</h3>
    <ul>
      <li *ngFor="let event of history">{{event}}</li>
    </ul>
  `,
  providers: [MissionService]
})
export class MissionControlComponent {
  astronauts = ['Lovell', 'Swigert', 'Haise'];
  history: string[] = [];
  missions = ['Fly to the moon!',
              'Fly to mars!',
              'Fly to Vegas!'];
  nextMission = 0;
  constructor(private missionService: MissionService) {
    missionService.missionConfirmed$.subscribe(
      astronaut => {
        this.history.push(`${astronaut} confirmed the mission`);
      });
  }
  announce() {
    let mission = this.missions[this.nextMission++];
    this.missionService.announceMission(mission);
    this.history.push(`Mission "${mission}" announced`);
    if (this.nextMission >= this.missions.length) { this.nextMission = 0; }
  }
}

AstronautComponent也会在其构造函数中注入服务。每个AstronautComponent都是MissionControlComponent的子级,因此会收到其父级的服务实例:

import { Component, Input, OnDestroy } from '@angular/core';
import { MissionService } from './mission.service';
import { Subscription }   from 'rxjs/Subscription';
@Component({
  selector: 'my-astronaut',
  template: `
    <p>
      {{astronaut}}: <strong>{{mission}}</strong>
      <button
        (click)="confirm()"
        [disabled]="!announced || confirmed">
        Confirm
      </button>
    </p>
  `
})
export class AstronautComponent implements OnDestroy {
  @Input() astronaut: string;
  mission = '<no mission announced>';
  confirmed = false;
  announced = false;
  subscription: Subscription;
  constructor(private missionService: MissionService) {
    this.subscription = missionService.missionAnnounced$.subscribe(
      mission => {
        this.mission = mission;
        this.announced = true;
        this.confirmed = false;
    });
  }
  confirm() {
    this.confirmed = true;
    this.missionService.confirmMission(this.astronaut);
  }
  ngOnDestroy() {
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();
  }
}
  

请注意,我们捕获subscription并取消订阅   AstronautComponent被摧毁。这是一个内存泄漏保护步骤。   此应用程序中没有实际风险,因为a的生命周期   AstronautComponent与应用程序本身的生命周期相同。那   在更复杂的应用程序中并不总是如此。

     

我们不会将此警卫添加到MissionControlComponent因为,因为   父,它控制MissionService的生命周期。

历史记录日志表明,在服务的推动下,邮件在父MissionControlComponentAstronautComponent子女之间双向传播:

enter image description here

测试

点击父MissionControlComponentAstronautComponent子项的点击按钮,验证历史符合预期:

// ...
it('should announce a mission', function () {
  let missionControl = element(by.tagName('mission-control'));
  let announceButton = missionControl.all(by.tagName('button')).get(0);
  announceButton.click().then(function () {
    let history = missionControl.all(by.tagName('li'));
    expect(history.count()).toBe(1);
    expect(history.get(0).getText()).toMatch(/Mission.* announced/);
  });
});
it('should confirm the mission by Lovell', function () {
  testConfirmMission(1, 2, 'Lovell');
});
it('should confirm the mission by Haise', function () {
  testConfirmMission(3, 3, 'Haise');
});
it('should confirm the mission by Swigert', function () {
  testConfirmMission(2, 4, 'Swigert');
});
function testConfirmMission(buttonIndex: number, expectedLogCount: number, astronaut: string) {
  let _confirmedLog = ' confirmed the mission';
  let missionControl = element(by.tagName('mission-control'));
  let confirmButton = missionControl.all(by.tagName('button')).get(buttonIndex);
  confirmButton.click().then(function () {
    let history = missionControl.all(by.tagName('li'));
    expect(history.count()).toBe(expectedLogCount);
    expect(history.get(expectedLogCount - 1).getText()).toBe(astronaut + _confirmedLog);
  });
}
// ...

然而,我也是角度2的新人,我想听听更有经验的开发人员的意见。