angularhttphttpclientangular18angular-in-memory-web-api

After migrating to Angular 18 with the new build system I got an error: Http failure during parsing


I have updated a project that I use as a library for my application from angular 17 to angular 18, and I have updated all the packages as usual. I have also accepted the migration to the new build system.

npm i typescript@5.5.4
ng update @angular/core@18 ng update @angular/cli@18 
ng update @angular-eslint/schematics@18 ng update @angular/material@18

and when I run the library I get this error in the browser console when I should view some data abd it does not appear:

Http failure during parsing for http://localhost:4200/api/dashboard

{
    "headers": {
        "normalizedNames": {},
        "lazyUpdate": null
    },
    "status": 200,
    "statusText": "OK",
    "url": "http://localhost:4200/api/dashboard",
    "ok": false,
    "name": "HttpErrorResponse",
    "message": "Http failure during parsing for http://localhost:4200/api/dashboard",
    "error": {
        "error": {},
        "text": "<!doctype html>\r\n<html lang=\"en\">\r\n<head>\n  <script type=\"module\" src=\"/@vite/client\"></script>\n\r\n  <meta charset=\"utf-8\">\r\n  <title>WidgetsApp</title>\r\n  <base href=\"/\">\r\n\r\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\r\n  <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\">\r\n  <link href=\"https://fonts.googleapis.com/icon?family=Material+Icons\" rel=\"stylesheet\">\r\n<link rel=\"stylesheet\" href=\"styles.css\"></head>\r\n<body>\r\n  <app-root></app-root>\r\n<script src=\"polyfills.js\" type=\"module\"></script><script src=\"scripts.js\" defer></script><script src=\"main.js\" type=\"module\"></script></body>\r\n</html>\r\n"
    }
}

This is the method I call to get the mock data to test the library, prints the first console.log but not the second one:

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type': 'application/json',
    'ngsw-bypass': 'true'
  })
};
      
getAllDashboard(): Observable<Dashboard[]> {
        const url = api/dashboard;
        console.log("TEST (DashboardAPI) getAllDashboard");
        return this._http.get<Dashboard[]>(url, httpOptions)
          .pipe(
            tap(dashboards => {
              console.log("TEST (DashboardAPI) getAllDashboard", dashboards);
            }),
            catchError(this._handleError<Dashboard[]>(`getAllDashboard url=${url}`))
          );
      }

I create a mock database with InMemoryDataService which implements InMemoryDbService, that works properly in my library, but when I run my other library that has installed the package where is implemented the method, is like de mock data is not working.

export class WidgetInMemoryDataService implements InMemoryDbService {

  createDb() {

        const dashboardConfig = [
            {
                id: '00000000-0000-0000-0000-000000000000',
                name: 'Default',
                creationDate: new Date(Date.now()),
                createdById: '00000000-0000-0000-0000-000000000000',
                updatedDate: new Date(Date.now()),
                updatedById: '00000000-0000-0000-0000-000000000000',

            }
        ];

    return { dashboard};
  }

I added this service in the imports of the module.ts:

HttpClientInMemoryWebApiModule.forRoot(WidgetInMemoryDataService, {
    dataEncapsulation: false,
    passThruUnknownUrl: true,
}),

I have also tried to add in the httpOptions responseType: 'text', responseType: 'text' as 'json' but it does not works. It looks like it is because of the change of HttpClientModule to provideHttpClient(withInterceptorsFromDi()). Since with HttpClientModule it works correctly, also without both. The strange thing is that in the other package (@my-lib/dashboards) it works fine with provideHttpClient(withInterceptorsFromDi()).

package.json:

{
  "name": "widgets-app",
  "version": "0.0.0",
  "license": "MIT",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "jest --coverage --verbose --silent",
    "lint": "ng lint",
    "build_prod": "ng build widgets --configuration production",
    "pack": "cd dist/widgets && npm pack",
    "package": "npm run build_prod && npm run pack",
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "^18.2.7",
    "@angular/cdk": "^18.2.7",
    "@angular/common": "^18.2.7",
    "@angular/compiler": "^18.2.7",
    "@angular/core": "^18.2.7",
    "@angular/forms": "^18.2.7",
    "@angular/material": "^18.2.7",
    "@angular/material-moment-adapter": "^18.2.7",
    "@angular/platform-browser": "^18.2.7",
    "@angular/platform-browser-dynamic": "^18.2.7",
    "@angular/router": "^18.2.7",
    "@asymmetrik/ngx-leaflet": "^18.0.1",
    "@mylib/dashboards": "file:../MyLib.Dashboards/dist/dashboards/mylib-dashboards-18.0.0.tgz",
    "@mylib/layout": "^18.0.2",
    "@ngx-translate/core": "^15.0.0",
    "@ngx-translate/http-loader": "^8.0.0",
    "@syncfusion/ej2-angular-pivotview": "^27.1.48",
    "angular-in-memory-web-api": "0.18.0",
    "echarts": "^5.4.3",
    "leaflet": "^1.9.4",
    "leaflet.markercluster": "^1.5.3",
    "moment": "^2.29.4",
    "ng-dynamic-component": "^10.7.0",
    "ngx-daterangepicker-material": "^6.0.4",
    "ngx-echarts": "^18.0.0",
    "ngx-skeleton-loader": "^9.0.0",
    "ngx-translate-multi-http-loader": "^18.1.0",
    "rxjs": "^7.8.1",
    "uuid": "^9.0.0",
    "zone.js": "~0.14.10"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^18.2.7",
    "@angular-eslint/builder": "^18.3.1",
    "@angular-eslint/eslint-plugin": "^18.3.1",
    "@angular-eslint/eslint-plugin-template": "^18.3.1",
    "@angular-eslint/schematics": "^18.3.1",
    "@angular-eslint/template-parser": "^18.3.1",
    "@angular/cli": "^18.2.7",
    "@angular/compiler-cli": "^18.2.7",
    "@ngneat/spectator": "^19.0.0",
    "@types/jest": "^29.5.13",
    "@types/leaflet": "^1.9.12",
    "@typescript-eslint/eslint-plugin": "^8.6.0",
    "@typescript-eslint/parser": "^8.6.0",
    "eslint": "^9.12.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-prettier": "^5.2.1",
    "jest": "^29.7.0",
    "jest-auto-spies": "^3.0.0",
    "jest-preset-angular": "^14.2.4",
    "jest-sonar": "^0.2.16",
    "ng-mocks": "^14.13.1",
    "ng-packagr": "^18.2.1",
    "prettier": "^3.3.3",
    "typescript": "~5.5.4"
  }
}

Solution

  • Make sure to import the HttpClient() provider before InMemoryWebApiModule when bootstrapping the application.

    bootstrapApplication(AppComponent, {
        providers: [
          provideHttpClient(withInterceptorsFromDi()),
            importProvidersFrom(BrowserModule, FormsModule, AppRoutingModule, 
            // The HttpClientInMemoryWebApiModule module intercepts HTTP requests
            // and returns simulated server responses.
            // Remove it when a real server is ready to receive requests.
            HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, { dataEncapsulation: false })),
            HeroService, MessageService
        ]
    });