Bad context binding within service

SherloxTV

I'm trying to set the token attribute on my AuthenticationService.

Here's my code:

authentication.service.ts

import { Injectable } from '@angular/core'
import { Http, Headers, Response } from '@angular/http'
import { Observable, BehaviorSubject } from 'rxjs'
import 'rxjs/add/operator/map'
import { Login } from '../../+authentication/shared/models'
import { config } from '../config'

@Injectable()
export class AuthenticationService {
  private token: string
  private _isConnected = new BehaviorSubject<boolean>(false)
  isConnected$ = this._isConnected.asObservable()

  constructor (private http: Http) {
    const currentToken = JSON.parse(localStorage.getItem('currentToken'))
    if (currentToken) {
      this.token = currentToken.token 
      this._isConnected.next(true) // work
    }
  }

  login (login: Login): Observable<any> {
    return this.http.post(config.BASE_URL + '/auth/login', login)
      .map((response: Response) => {
        if (response.json() && response.json().success) {
          const token = response.json() && response.json().token
          this.token = token// don't do the work
          this._isConnected.next(true) // don't do the work neither

          localStorage.setItem('currentToken', JSON.stringify({token}))

          return {
            success: true
          }
        } else if (response.json() && response.json().message) {
          return {
            success: false,
            message: response.json().message
          }
        }
      })
  }

  logout (): void {
    this.token = null
    this._isConnected.next(false) // work
    localStorage.removeItem('currentToken') // work
  }

  logToken (): void {
    console.log(this.token) // null
    console.log(localStorage.getItem('currentToken')) // {"token":"eyJhb..."}

    // do the work
    this.token = localStorage.getItem('currentToken')
    console.log(this.token)

    // do the work too
    this._isConnected.next(true)
  }
}

Called from login.component.ts

import { Component, OnInit } from '@angular/core'
import { Router } from '@angular/router'

import { AuthenticationService } from '../../shared/services'

import { Login } from '../shared/models/index'

@Component({
  selector: 'login',
  templateUrl: './login.component.html',
})
export class LoginComponent {
  public login: Login = new Login('[email protected]', 'test')
  public authError: string

  constructor (
    private router: Router,
    private as: AuthenticationService
  ) {
    if (as.getToken()) {
      this.router.navigate(['/home']) // This work !! That's not 
    }
  }

  onSubmit () {
    this.as.login(this.login)
      .subscribe((result) => {
        if (result.success) {
          this.router.navigate(['/home']) // working
        } else if (result.message) {
          this.authError = result.message // working
        }
      })
  }
}

At the end of authentication.service.ts, you can see the logToken function:

logToken (): void {
    console.log(this.token) // null
    console.log(localStorage.getItem('currentToken')) // {"token":"eyJhb..."}

    // do the work
    this.token = localStorage.getItem('currentToken')
    console.log(this.token)

    // do the work too
    this._isConnected.next(true)
  }

I call it manually from my app component by clicking a button in the navbar, and the token property is never set, unless I reload the page, in that case it's set by the constructor of the service, who gets it from LocalStorage.

Please tell me why the let don't want to work, I used the arrow-function to bind it but it seems it's not working.

Thanks in advance

SherlockStd

EDIT

I tried to delegate the response handling to a private method to see if the this binding is better, but it's still not working (authentication.service.ts):

  login (login: Login): Observable<any> {
    return this.http.post(config.BASE_URL + '/auth/login', login)
      .map((response: Response) => this.handleResponse(response))
  }

  private handleResponse (response: Response) {
    if (response.json() && response.json().success) {
          // login successful if there's a jwt token in the response
      const token = response.json() && response.json().token
      this.token = token

      localStorage.setItem('currentToken', JSON.stringify({token}))

      return {
        success: true
      }
    } else if (response.json() && response.json().message) {
      return {
        success: false,
        message: response.json().message
      }
    }
  }

EDIT 2

I logged the this keyword from authentication.service.ts, inside the login() and logToken() functions. It return an AuthenticationService each times, containing the same attributes but into the login function it contains the token attribute, which is lossed outside this function.

SherloxTV

After 3 days of research, I finally found the source of this problem.

I was declaring the same service in the parent (app.module.ts) and the child (authentication.module.ts) modules (see the structure below).

src/
├── app.component.html
├── app.component.ts
├── app.module.ts <== provide AuthenticationService
├── app.routes.ts <== load authentication.module with router.loadChildren()
│
├── +authentication/
│   ├── authentication.component.html
│   ├── authentication.component.ts
│   ├── authentication.module.ts <== provide AuthenticationService
│   ├── authentication.routes.ts <== expose a route to login component
│   └── login
│       ├── login.component.html
│       └── login.component.ts <== using service's methods here don't work
│
└── shared/
    └── services/
        ├── authentication.service.ts
        └── base.service.ts

I can't understand why it influenced the this binding, but it's working now, and I opened an issue on Github because I think an error should throw up in that case.

Thanks for your help.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related