Search code examples
angulardependency-injectionrxjscallback

Passing an Injectable service function as a callback function parameter


What I am trying to achieve is to first get the data, then get the sync details to decide when I should get the data again. Here I left out the details of the logic as that was not the cause of the issue.

The issue is when I am trying to pass an Injectable service function as a callback function parameter.

When this was executed, it says: ERROR Error: Cannot read properties of undefined (reading 'http')

This led me to believe that HttpClient wasn't injected by the time I call the callback.

Is it incorrect to pass an injectable service function as a callback function parameter? Is there any way to overcome this?

Code on Stackblitz

export class DataComponent implements OnInit {
  constructor(
    private dataService: DataService,
    private syncService: SyncService
  ) {}

  ngOnInit(): void {
    this.getData().subscribe();
  }

  getData() {
    return this.dataService.getData().pipe(
      tap((res) => console.log('get first data', res)),
      switchMap(() =>
        this.syncService.getSync(this.dataService.getSyncDetails, this.getData)
      )
    );
  }
}
@Injectable({
  providedIn: 'root',
})
export class SyncService {
  constructor() {}

  getSync(syncService: Function, callbackService: Function): Observable<any> {
    console.log('getSync');
    return syncService().pipe(
      tap((res) => console.log('get sync data', res)),
      switchMap(() => callbackService())
    );
  }
}
@Injectable({
  providedIn: 'root',
})
export class DataService {
  constructor(private http: HttpClient) {}

  getData(): Observable<any> {
    console.log('getData');
    return this.http.get('https://reqres.in/api/users');
  }

  getSyncData(): Observable<any> {
    console.log('getSyncData');
    return this.http.get('https://regres.in/api/unknown/2');
  }
}

Solution

  • You are providing a function only, which switches the context of a function. To keep the service context you can bind the function to the service, when passing it.

    getData() {
        return this.dataService.getData().pipe(
          tap((res) => console.log('get first data', res)),
          switchMap(() =>
            this.syncService.getSync(this.dataService.getSyncDetails.bind(this.dataService), this.getData)
          )
        );
      }