Search code examples
angulartypescriptangular-servicesangular-components

inject one service into another and have access to the injected services members angular 7


everything I'm reading here: https://angular.io/guide/dependency-injection-in-action makes this seem like it should be possible, but I get this error when trying to call a method on my injected service from another service:

TypeError: Cannot read property 'isLoggedIn' of undefined at CatchSubscriber.handleError [as selector] (project.service.ts:30) at CatchSubscriber.error (catchError.js:29) at TapSubscriber._error (tap.js:56) at TapSubscriber.error (Subscriber.js:55) at MapSubscriber._error (Subscriber.js:75) at MapSubscriber.error (Subscriber.js:55) at FilterSubscriber._error (Subscriber.js:75) at FilterSubscriber.error (Subscriber.js:55) at MergeMapSubscriber.notifyError (OuterSubscriber.js:7) at InnerSubscriber._error (InnerSubscriber.js:14)

Here is my ProjectService which is injecting my UserService

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { Observable, throwError, of } from 'rxjs';
import { catchError, tap, map } from 'rxjs/operators';
import { AuthHeader } from '../../Shared/Helpers/AuthHeader';
import { UserService } from '../../User/Services/user.service';

@Injectable({
  providedIn: 'root'
})
export class ProjectService {

  constructor(private http: HttpClient, private userService: UserService) {

  }
  private projectsApiEndPointRoot = '/api/ProjectApi/';
  test(): Observable<number> {
    return <Observable<number>>this.http.get<number>(this.projectsApiEndPointRoot + "getimages/8", { headers: AuthHeader.getAuthHeader() }).pipe(
      tap(data => console.log(data)),
      catchError(this.handleError)
    );
  }

  private handleError(err: HttpErrorResponse) {
    let errorMessage = '';
    if (err.error instanceof ErrorEvent) {
      errorMessage = `An error occurred: ${err.error.message}`;
    } else {
      if (err.status == 401 || err.status == 403) {
        console.log(this.userService.isLoggedIn());
      }
      else {
        errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
      }
    }
    console.error(errorMessage);
    return throwError(errorMessage);
  }
}

The UserService just has a method that should return boolean whether or not the user is logged in. I use that same method on several components with success, but it's not working when trying to call it from this other service.

Is this not possible to do?

EDIT: adding bits of UserService for demonstration:

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError, of, BehaviorSubject } from 'rxjs';
import { catchError, tap, map } from 'rxjs/operators';
import { EmailModel } from '../Models/EmailModel';
import { ILoginRequest } from '../Interfaces/ILoginRequest';
import { ILoginResponse } from '../Interfaces/ILoginResponse';
import { IRegistrationRequest } from '../Interfaces/IRegistrationRequest';
import { Router } from '@angular/router';


@Injectable({
  providedIn: 'root'
})
export class UserService {
  constructor(private http: HttpClient, private router: Router) { }
  private usersApiEndPointRoot = 'api/UserApi/';

  private loggedIn = false;
  ...

  isLoggedIn() {
    return this.loggedIn;
  }


  private handleError(err: HttpErrorResponse) {
    let errorMessage = '';
    if (err.error instanceof ErrorEvent) {
      errorMessage = `An error occurred: ${err.error.message}`;
    } else {
      errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
    }
    console.error(errorMessage);
    return throwError(errorMessage);
  }
}

Solution

  • The problem is that you're passing the function on the user service. When it is called it the value of this will be the window.

    Instead of passing the function to the catchError function, use a lambda function so that this will be preserved. Make following change to the test() method and your problem will be solved:

    test(): Observable<number> {
      return <Observable<number>>this.http.get<number>(this.projectsApiEndPointRoot + 
          "getimages/8", { headers: AuthHeader.getAuthHeader() }).pipe(
        tap(data => console.log(data)),
        catchError(err => this.handleError(err)) // <--
      );
    }