Angular2 - 'watch' provider property in mu

2019-06-23 15:31发布

问题:

I'm coming form NG1 environment and currently I'm creating NG2 app with all available features. Before asking I was exploring google and stackoverflow questions but with no luck since angular 2 moved so fast with api architecture and most answers are out of date.

My case: I have Auth provider (service) with property user, I would like to observe user and react in multiple components (navbar, sidebar etc.)

What I tried:

@Injectable();
export class Auth {

    private user;
    authorized: EventEmitter<boolean>;

    constructor(public router:Router){
        this.authorized = new EventEmitter<boolean>();
    }

    login(user, token):void{
        localStorage.setItem('jwt', token);
        this.user = _.assign(user);

        this.authorized.emit(<boolean>true);
        this.router.parent.navigateByUrl('/');
    }
}


/***************/
@Component({...})
export class NavComponent {

     public isAuthroized: boolean = false;

     constructor(Auth:Auth){
         Auth.authorized
             .subscribe((data) => this.onUserChanged(data));
     }

     onUserChanged(user){
        alert('USER:' + user.email);
        this.isAuthroized = true;
     }
}

/****************/
bootstrap(AppComponent, [
     ROUTER_PROVIDERS,
     ELEMENT_PROBE_PROVIDERS,
     HTTP_PROVIDERS,
     MapLoader,
     Auth
])

But with no luck. Should I use Observable EventEmitter or maybe there is other correct approach to handle this case? In NG1 it would be as simple as set $watch on service's property. Thanks!

EDIT: I added new method to Auth service:

...
userUpdated: EventEmitter<boolean>;

constructor(public router:Router){
    this.userUpdated = new EventEmitter<any>();
}

...

logout(returnUrl?: string): void{
    delete this.user;
    localStorage.removeItem('jwt');
    this.userUpdated.emit(undefined);

    if(!_.isEmpty(returnUrl)){
        this.router.parent.navigateByUrl(returnUrl);
    }

}

And now event is called, why is this working for logout and not for login?

EDIT 2:

export class LoginPageComponent {
   error: string;

   constructor(public http: Http, public router: Router, public Auth:Auth){
   }

   login(event, email, password){
      ...
      this.http.post('/api/login', loginModel, {headers: headers})
        .map((res) => res.json())
        .subscribe((res: any) => {
            this.Auth.login(res.user, res.ADM_TOKEN);
        }, (error) => this.error = error._body);
   }

}

RESOLVED

Silly mistake.. i left in NavComponent in providers array [Auth].. so it was different object than global Auth.. sorry guys! Hope this issue will help somebody new in Angular2. Thanks for your effort.

回答1:

I assume you are adding Auth as provider to every component. This creates a new instance of the class for each component. Add it only in bootstrap(AppComponent, providers: [...]) or only on the AppComponent.



回答2:

You can notice that there is no watch feature in Angular since it's now managed by Zones.

To notify other components, you could add another EventEmitter within your Auth service for the userUpdated event. The components that want to be notified could register on this emitter.

@Injectable();
export class Auth {
  private user;
  authorized: EventEmitter<boolean>;
  userUpdated: EventEmitter<boolean>;

  constructor(public router:Router){
    this.authorized = new EventEmitter<boolean>();
    this.userUpdated = new EventEmitter<boolean>();
  }

  login(user, token):void{
    localStorage.setItem('jwt', token);
    this.user = _.assign(user);

    this.authorized.emit(<boolean>true);
    this.userUpdated.emit(this.user);
    this.router.parent.navigateByUrl('/');
  }
}

Components could register on this event:

@Component({...})
export class NavComponent {
  (...)
  constructor(Auth:Auth){
    Auth.userUpdated
         .subscribe((data) => this.onUserChanged(data));
  }
}

You could also provide the user when emitting the authorized event:

  login(user, token):void{
    localStorage.setItem('jwt', token);
    this.user = _.assign(user);

    this.authorized.emit(this.user);
    this.router.parent.navigateByUrl('/');
  }
}

Hope it helps you, Thierry