19

I have an authentication service that makes the authenticated variable equal to true or false.

checkAuthentication(){
  this._authService.getAuthentication()
    .subscribe(value => this.authenticated = value);
}

How do I execute a function when this.authenticated has changed value? ngOnChanges is not picking up the change.

Sasxa
  • 36,894
  • 14
  • 85
  • 98
ClickThisNick
  • 4,680
  • 8
  • 32
  • 59

4 Answers4

29

To keep authenticated in service and share it between components you can use BehaviorSubject, it's value to check authentication in different places, and it's subscribe() method to react on change...

class AuthService {
  public authenticated = new BehaviorSubject(null);
  getAuthentication() {
    this._http.get('/authenticate')
      .map(response => response.json())
      .map(json => Boolean(json)) // or whatever check you need...
      .subscribe((value: boolean) => this.authenticated.next(value))
  }
}

class Component {
  constuctor(private _authService: AuthService) {
    // Only check once even if component is 
    // destroyed and constructed again
    if (this._authService.authenticated.value === null)
      this._authService.getAuthentication();
  }

  onSubmit(){
    if (!this._authService.authenticated.value)
      throw new Error("You are authenticated!")
  }
}

How do I execute a function when this.authenticated has changed value?

  this._authService.authenticated
   .subscribe((value: boolean) => console.log(value))
Sasxa
  • 36,894
  • 14
  • 85
  • 98
  • Hi @Sasxa so if I have e.g. 5 components. `home.component.ts`, `footer.component.ts`, `navbar.component.ts`, `x.component.ts`, and `user.component.ts`. Assuming these 5 components, and if I want to check for authenticated. I'll have to add inject the following: `constructor(private _authService: AuthService) {...}` to all my 5 components. Is this correct? and do a `ngOnInit` for all 5 components? Need some help to understand Angular2 authentication concept. – choopage - Jek Bao Feb 22 '16 at 01:25
  • 1
    There are many options/choices.. It all depends on what you're doing. You can authenticate at every component, or at root component only. It's a complex topic, you'll need to decide what's best for your app (; – Sasxa Feb 22 '16 at 02:24
  • could you point me to discussion/forum/blog/article on this topic? – choopage - Jek Bao Feb 22 '16 at 06:38
  • This doesn't actually seem to be working with the most recent version of Angular 2. Anyone find any solutions? – tmanion Feb 28 '16 at 05:00
11

I think that you could leverage the get / set syntax of TypeScript to detect when your authenticated property of your service is updated:

  private _authenticated:Boolean = false;
  get authenticated():Boolean {
        return this._authenticated ;
  }
  set authenticated ( authenticated Boolean) {
    // Plugin some processing here
      this._ authenticated = authenticated;
  }

When assigning a value, the "set authenticated" block is called. For example with such code:

this.authenticated = true;

See this question for more details:

That said you could also leverage an EventEmitter property in the service. When the authenticated property is updated, the corresponding event could be fired.

export class AuthService {
  authenticatedChange: Subject<boolean> = new Subject();

  constructor() {}

  emit(authenticated) {
    this.authenticatedChange.next(authenticated);
  }

  subscribe(component, callback) {
    // set 'this' to component when callback is called
    return this.authenticatedChange.subscribe(data => {
      callback(component, data);
    });
  }
}

See this link for more details:

Community
  • 1
  • 1
Thierry Templier
  • 182,931
  • 35
  • 372
  • 339
  • Is it good approach to go with, as you are suggesting to fire up some event on while setting `authenticated` value? Ideally setter are responsible for setting up value by manipulating the provided input value to its function.. – Pankaj Parkar Feb 21 '16 at 19:38
  • Hi Thierry, I searched Angular.io site for documentation/tutorial on EventEmitter. But it was quite limited. Any idea where I can find guided tutorial on using Angular2 EventEmitter? – choopage - Jek Bao Feb 22 '16 at 01:10
  • 1
    this is considered bad practice. recommended approach is to use Subject/BehaviorSubject – atsituab Jun 27 '16 at 17:09
  • @atsituab you're definitely right. I answered this a long time ago ;-) I updated my answer accordingly! Thanks for pointing this out! – Thierry Templier Jun 27 '16 at 19:54
1

It depends on who needs to handle the event. If it's the parent component, you can leverage output event bindings:

@Output authenticationChange: EventEmitter<Boolean> = new EventEmitter();
checkAuthentication(){
     this._authService.getAuthentication()
     .subscribe(value => 
           if(value != this.authenticated) {
               this.authenticated = value);
               this.authenticationChange.emit(value);
      });
 }

And in your parent component:

 <directive (authenticationChange)="doSomething()">
pixelbits
  • 48,078
  • 15
  • 86
  • 124
-2

I used a {{ showValue() }} in the component template, and in the .ts file I called the service's variable

showValue() {
   this.authenticated = this._authService.authenticated;
   return "dummy"
} 

Thanks to Angular2's GUI 2-way binding, it works.

recursion
  • 15
  • 2