0

I am using Angular 7 at the moment.

Working through the angular web page on dynamic forms https://angular.io/guide/dynamic-form

want to use the inMemoryWebAPI to store the data. following 2 different tutorials on inMemoryWebApi https://www.techiediaries.com/angular-inmemory-web-api/ and https://medium.com/@coderonfleek/faking-the-backend-in-angular-apps-a10c1b8823c

I seem to be missing some piece of the plumbing. I am only getting a 404 error in the console.

I have the data defined in a file named sb-doc-field.data.service.ts

import { Injectable } from '@angular/core';
import { InMemoryDbService } from 'angular-in-memory-web-api';

@Injectable({
  providedIn: 'root'
})
export class sbDocFieldDataService implements InMemoryDbService {

  constructor() { }

 createDb() {

//these 4  fields belong to the same  docInstance
let docFields = [
  {   id: 10, fieldName: 'Property Name', description: 'Property Name', fieldType: 'text' }
  , {   id: 11, fieldName: 'Proeprty Type', description: 'MF/SF/COMM', fieldType: 'list' }
  , {   id: 12, fieldName: 'Close Date', description: 'When will you close?', fieldType: 'date' }
  , {   id: 13, fieldName: 'Purchase Price', description: 'Suggested Price', fieldType: 'number' }
];
return { docFields: docFields  };

} }

My data service looks like this

  import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { InMemoryDbService} from 'angular-in-memory-web-api'
import { catchError, map, tap } from 'rxjs/operators';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

  //class will provide crud API for field meta data used when building docs
export class sbDocFieldService {

  SERVER_URL: string = "http://fakeUrl.DoesntMatter/api/";
  docFieldsName: string = "docFields";
  constructor(private httpClient: HttpClient) {
  }

  public getFieldsForDocument() {
    return this.httpClient.get<any[]>(this.SERVER_URL + this.docFieldsName )
      .pipe(
        catchError(this.handleError<any[]>('getFieldsForDocument', []))
      );;
  }

  public getField(fieldId) {
    return this.httpClient.get(`${this.SERVER_URL}${this.docFieldsName}/${fieldId}`);

  }

//this error handler is a straight up copy/paste from the angular page on InMemWebApi private handleError(operation = 'operation', result?: T) { return (error: any): Observable => {

      // TODO: send the error to remote logging infrastructure
      console.error(error); // log to console instead

      // TODO: better job of transforming error for user consumption
      console.log(`${operation} failed: ${error.message}`);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  } //handleError
}

and my component looks like this

    import { Component, OnInit } from '@angular/core';
import { sbDocFieldService } from '../sb-doc-field.service';

@Component({
  selector: 'app-analysis',
  templateUrl: './analysis.component.html',
  styleUrls: ['./analysis.component.css']
})
export class  AnalysisComponent implements OnInit {

  fields: any[] = [];
  constructor(private fieldService: sbDocFieldService) { }

  ngOnInit() {
    this.getAllDocFields();
  }  //ngOnInit

  getAllDocFields() {
    this.fieldService.getFieldsForDocument(4).subscribe(
      (data: any[]) => {
        console.log(data);
        this.fields = data;
      });

  }  //getAllDocFields

}

and parts of app.module.ts

imports: [
    BrowserModule,
    CommonModule,
    FormsModule,
    ReactiveFormsModule,
    BrowserAnimationsModule,
   RouterModule.forRoot(routes),  //, { enableTracing: true }
    HttpClientModule,
           InMemoryWebApiModule.forRoot(sbDocFieldDataService),
    HttpClientInMemoryWebApiModule.forRoot(sbDocFieldDataService)
  ],
  providers: [
    EventService,
    {
      provide: SLIMSCROLL_DEFAULTS,
      useValue: {
        alwaysVisible : false
      }
    },
    sbDocFieldDataService
  ],

I am not sure I need the two entries in imports[]. I am not sure I need my service to be a provider, but I think I do....

When I refresh the page, the data service is returning a 4 element array.

When the logic gets back to my subscription call, there is an array, but it is empty.

I am making sure that the array name = the url token 'docFields', but changing the url token does not really affect anything.

The 404 has to be some part of the url, but I am not seeing it. I have used the urls in the examples, but I also see where they say it does not matter.

I have reviewed the possible duplicate question. I was simply returning an array. I have updated my code listing to include currently

return {docFields}

i have also tried

return {docFields : docFields[]}
and 
return {docFields : any[]}
and 
return {docFields : docFields}
and 
return {docFields : any}

What am I doing/not doing that is causing the WebAPI to yield a 404?

2
  • 1
    Possible duplicate of InMemoryWebApiModule - status: 404 - collection not found Commented Sep 9, 2019 at 14:06
  • thanks, Idid need to correct my return statement. The problem still exists. I have updated my posting to include things i tried Commented Sep 9, 2019 at 15:05

1 Answer 1

1

The problem was that I was returning the array and not an object from the data.service object.

Another easy to overlook piece of the InMemoryWebAPI is that the url you use has to correspond to the name of the array

ex

 let docFields = [
  {   id: 10, fieldName: 'Property Name', description: 'Property Name', fieldType: 'text' }
  , {   id: 11, fieldName: 'Proeprty Type', description: 'MF/SF/COMM', fieldType: 'list' }
  , {   id: 12, fieldName: 'Close Date', description: 'When will you close?', fieldType: 'date' }
  , {   id: 13, fieldName: 'Purchase Price', description: 'Suggested Price', fieldType: 'number' }
];
return { docFields: docFields  };

in your service, you have to ask for

http://someurl/api/docFields

in playing w/ the code trying to identify what things were affecting the result i had changed the service so the docFieldsName = "docFieldsX" this neither seemed to hurt or help so i left it. Once the duplicate answer suggested I change the return type, this was now a new problem. argh.

SERVER_URL: string = "http://fakeUrl.DoesntMatter/api/";
  docFieldsName: string = "docFieldsX";
  constructor(private httpClient: HttpClient) {
  }

  public getFieldsForDocument() {
    return this.httpClient.get<any[]>(this.SERVER_URL + this.docFieldsName )
      .pipe(
        catchError(this.handleError<any[]>('getFieldsForDocument', []))
      );;
  }

Ultimately , having an error handler on the service calls was what helped me resolve the problem.

this is that code. If you are having a similar problem, consider including this code in your service so you can get a better Idea what the issue is.

public getDocFields() {
    return this.httpClient.get<any[]>(this.SERVER_URL + this.docFieldsName )
      .pipe(
        catchError(this.handleError<any[]>('getFieldsForDocument', []))
      );;
  }


  private handleError<T>(operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      // TODO: better job of transforming error for user consumption
      console.log(`${operation} failed: ${error.message}`);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  } //hanldeError

While the problem was the return type in my InMemoryDB file the returned error looked like this body.error: "Collection'docFields' not found" status : 404 statusText : "Not Found" url : "http://fakeUrl.doesntMatter/api/docFields"

hope this helps someone else.

@Kirk Larkin, i dont know how to give you credit other than +1 on the comment. You put me on the path.

Sign up to request clarification or add additional context in comments.

1 Comment

I'm glad I helped. You can upvote the answer I linked to if you like. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.