0

I have *ngFor in my template:

<mat-card *ngFor="let cargo of cargos" class="cont-mat">
...

    <mat-checkbox *ngFor="let truck of getTrucksByUserIdAndRules(cargo.id)" formControlName="truckId">{{truck.regNumber}}</mat-checkbox>

component function:

getTrucksByUserIdAndRules(cargoId: string) {
   return this.cargosService.getTrucksByUserIdAndRules(this.userId, cargoId);
  }

and cargoService function:

getTrucksByUserIdAndRules(userId: string, cargoId: string): Observable<any> {
    return this.http.get(BACKEND_URL_2 + "getTrucksByUserIdAndRules/" + userId + "/" + cargoId);
  }

I'm getting error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays.

I want to fetch only specific trucks for each cargo.id. That's why I'm using function getTrucksByUserIdAndRules(cargo.id) instead of defined array. Thank you for your time, any other solution than this is welcome.

2
  • 1
    It tries to loop through the observable itself, not the result from it. That's why you get that error. You need to use the async pipe or subscribe to the observable and store the result in a variable Commented Sep 23, 2021 at 12:02
  • What are you returning from getTrucksByUserIdAndRules(cargo.id) ? You need to add those details as it will be tough to give you any answer without it. It seems you are giving object to the ngFor. Commented Sep 23, 2021 at 12:25

2 Answers 2

1

You need to add <any> after the get in the cargoService component

getTrucksByUserIdAndRules(userId: string, cargoId: string): Observable<any> {
    return this.http.get<any>(BACKEND_URL_2 + "getTrucksByUserIdAndRules/" + userId + "/" + cargoId);
  }

This way it will check that is an object that has a length and can do the for.

Also, try to add the .suscribe() to the function and adding the result to a variable so you can operate with that variable:

        getTrucksByUserIdAndRules(cargoId: string) {
          return this.cargosService.getTrucksByUserIdAndRules(this.userId, cargoId).suscribe(
             res=> this.variable = res //Try checking what it gets. Try operating with the variable instead
          );
       }
Sign up to request clarification or add additional context in comments.

2 Comments

still error after putting <any>
Okay I added something more that could help you
1

The problem

  1. Your response you are getting from the API seems be not an array which is required for an iteration. What is the response from this request?
return this.http.get(BACKEND_URL_2 + "getTrucksByUserIdAndRules/" + userId + "/" + cargoId);
  1. Since your request returns an Observable, which is async, you need to add an async pipe in your template. getTrucksByUserIdAndRules(cargo.id) is an Observable
*ngFor="let truck of getTrucksByUserIdAndRules(cargo.id)"

The solution

  1. You need to check first what your response value is and then map it to an array if necessary. This is done via a rxjs pipe. You need to modify your service method. It would look like this:

Docs to pipe: https://rxjs.dev/guide/operators

getTrucksByUserIdAndRules(userId: string, cargoId: string): Observable<any> {
  const url = BACKEND_URL_2 + "getTrucksByUserIdAndRules/" + userId + "/" + cargoId;
  return this.http.get(url).pipe(
     map(response => {
        // console.log(response) <- check here your value, first
        return response.value // this is just an example here, but you need to return an array at this point
     })
  );
}
  1. You need to add the async pipe: https://angular.io/api/common/AsyncPipe

For that, update your template like this:

<mat-checkbox *ngFor="let truck of (getTrucksByUserIdAndRules(cargo.id) | async)" formControlName="truckId">{{truck.regNumber}}</mat-checkbox>

Notes:

  • I would recommend to put the result from the request into a seperate variable (attribute) of the component instead of calling the method directly in the template to make it look more clean. Component method:
trucks$: Observable<any[]>;
getTrucksByUserIdAndRules(cargoId: string) {
   this.trucks$ = 
  this.cargosService.getTrucksByUserIdAndRules(this.userId, cargoId)
  return this.trucks$;
}

Template:

<mat-checkbox *ngFor="let truck of trucks$" formControlName="truckId">{{truck.regNumber}}</mat-checkbox>
  • Also use the exact type instead of any since this will create problems in the future as the application grows.

If you still have problems, don't hesitate to ask. Here to help :)

Edit, after question updated:

Iterating and calling functions in the template is not a good practice as I mentioned above, so basically the best would be to put it into the ngOnInit and init everything there so it will not refetch when it rerenders:

ts file

cargoData: Observable<any[]>[];

ngOnInit() {
  this.cargoData = this.cargos.map(cargo => this.getTrucksByUserIdAndRules(cargo.id));
}

html file

<mat-card *ngFor="let trucks of cargoData" class="cont-mat">
...

    <mat-checkbox *ngFor="let truck of (trucks | async)" formControlName="truckId">{{truck.regNumber}}</mat-checkbox>

7 Comments

Thank you Ling. I have implemented your solution, and I'm getting data from the backend but it's infinite loop
With no details, it's quite hard to guess. This approach definately does not end in a infinite loop. The function is called once. Maybe check where is it called.
Or even better fetch it in the ngOnInit and then use it instead of fetching it while it loops.
Function getTrucksByUserIdAndRules(cargo.id) must be in template because it needs parameter cargo.id. I have updated my question. For every cargo there's a different truck data.
Still don't see why it ends up in a infinite loop bth.. You might check your logic twice.
|

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.