1

This is in an Angular 7 project and the code was written maybe 5-6 years ago, and I'm trying to update the application to latest Angular. Right now I'm just testing the login portion of the app which is basic http authentication. This is the last piece I need to refactor but im pretty lost. I don't want to break the entire system or do a full revamp just get something working so the whole ui can be upgraded from angular 7 to 14.

This is the old code below.

import { ConnectionBackend, Http, Request, RequestOptions, RequestOptionsArgs, Response, XHRBackend } from '@angular/http';
import { AuthService } from './../auth/auth.service';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable()
export class AuthHttp extends Http {
    constructor(backend: XHRBackend, defaultOptions: RequestOptions, private auth : AuthService) {
        super(backend, defaultOptions);
        this.auth = auth;
    }
    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
        // hack to deal with compatibility issue of rxjs versions
        // see https://stackoverflow.com/questions/38168581/observablet-is-not-a-class-derived-from-observablet
        let optionsAny = <any>options;
        let opts: RequestOptionsArgs = {
            method: optionsAny.method,
            body: optionsAny.body,
            withCredentials: true,
            headers: optionsAny.headers,
        }
        let query = '';
        for (let k of Array.from(optionsAny.params.paramsMap.keys())) {
            let v = optionsAny.params.paramsMap.get(k);
            query += '&' + k + '=' + encodeURIComponent(v);
        }
        if (query) {
            url = url + '?' + query.substring(1);
        }
        return Observable.create(sub => {
            super.request(url, opts).subscribe(
                res => { sub.next(res); },
                err => {
                    if (err.status === 401) {
                        this.auth.signout();
                    } else {
                        sub.error(err);
                    }
                } );
        });
    }
}

I tried some stuff but it all seems to lead to no where. I'm not really sure where to start. This is my first time using angular and it's a decently sized project. I feel like there's a really simple way to do this, but I'm not totally sure, I've been researching and haven't found anyone quite doing anything like this.

It compiles with no errors with an empty class. Like below, these imports are all the equivalent or roughly equivalent for the new angular httpclientmodule I believe .

import { AuthService } from './../auth/auth.service';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { HttpClient, HttpParams, HttpHeaders, HttpXhrBackend, HttpResponse, HttpBackend, HttpRequest, HttpEvent } from '@angular/common/http';
@Injectable()
export class AuthHttp extends HttpClient {}

Then I get 2 errors below in the console of my browser when i do ng serve with the blank class above.

ERROR NullInjectorError: R3InjectorError(AppModule)[ApiService -> ApiService -> ApiService]: 
  NullInjectorError: No provider for ApiService!
    Angular 9
    AppComponent_Factory app.component.ts:11
    Angular 26
    631 main.ts:11
    Webpack 7
core.mjs:6362:22
    Angular 16

Any help on where to start or helpful resource is appreciated thank you.

This is how ApiService is implemented

@Injectable()
export class ApiService {
  private apiUrl: string;

  constructor(private authHttp: AuthHttp, private auth: AuthService) {}

  getUrl() {
    return this.apiUrl;
  }

  setUrl(url: string) {
    this.apiUrl = url;
  }

  getConfiguration(): Configuration {
    const token = this.auth.getToken();
    return new Configuration({
      accessToken: token
    });
  }

  getUserApi(): UserService {
    return new UserService(this.authHttp, this.apiUrl, this.getConfiguration());
  }

  getProductionApi(): ProductionService {
    return new ProductionService(
      this.authHttp,
      this.apiUrl,
      this.getConfiguration()
    );
  }
Daniel
  • 15
  • 1
  • 4

2 Answers2

0

Without seeing the implementation of ApiService and its difficult to identify the issue. Broadly speaking it seems to me like the ApiService isn't provided from any module.

A quick check would be to provide it to the root of the application. @Injectable({ providedIn: 'root', }) export class ApiService ....

Craig
  • 11
  • 1
  • 1
    Okay I edited my post to add the ApiService. There's a few more methods not shown, but I don't think they're needed. – Daniel Jul 19 '22 at 22:43
  • `@Injectable() export class ApiService {` Means the ApiService is provided from a module. From the files you shared I cant tell if you provide it correctly. `@Injectable({ providedIn: 'root', }) export class ApiService ....` explicitly makes the service availbel to the whole application. This is attempting to resolve the `R3InjectorError`. – Craig Jul 20 '22 at 20:03
  • It should be working fine. Now if my AuthHttp class above is empty everything compiles fine and no errors in console. I can access the login page locally but im not able to login of course. I see this old post http://www.adonespitogo.com/articles/angular-2-extending-http-provider/ is doing something similar to my old code i need to update, but im pretty lost on what it's doing without more research into httpclient modules and angular. – Daniel Jul 20 '22 at 23:48
0

Ok I got it working but I'm still not totally sure how it's working or what it's doing. Basically the old method was doing this in a complicated why because of the limitations of older angular 2.

@Injectable()
export class AuthHttp extends HttpClient implements HttpInterceptor {
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const authReq = req.clone({
            withCredentials: true
        });
        return next.handle(authReq);
    }
}
Daniel
  • 15
  • 1
  • 4