Search code examples
angulartypescript

Angular async pipe with @for


I am using latest angular version 18.1.0 and want to use async pipe for an array of countries fetched from the backend using http client. But while using new angular feature of @for it give me error:

Type 'Country' must have a 'Symbol.iterator' method that returns an iterator.

My code is as follows country.component.html

<main>
    <select name="countries" id="countries-selection">
        @for (country of countries$ | async; track $index) {
        <option value="{{country}}">{{ country }}</option>
        }
    </select>
</main>

country.component.ts

import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';
import { Country } from './country.model';
import { Observable, map } from 'rxjs';
import { CommonModule } from '@angular/common';


@Component({
  selector: 'app-country',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './country.component.html',
  styleUrl: './country.component.css'
})
export class CountryComponent {

  countries$: Observable<Country>;

  constructor(private httpClient: HttpClient) {
      this.countries$ = this.httpClient.get('http://localhost:3000/countries').pipe(
      map((response: any) => <Country> response.json()));

  }

}

EDIT: I am getting following array from the server:

[
    {
      "id": "1",
      "name": "Australia",
      "isCurrent": true,
      "timezone": "Asia/Karachi"
    },
    {
      "id": "2",
      "name": "Canada",
      "isCurrent": true,
      "timezone": "Asia/Karachi"
    },
    {
      "id": "3",
      "name": "Finland",
      "isCurrent": false,
      "timezone": "Europe/Tallinn"
    }

]

What could be the possible solution if I want to use rxjs and not JS async/await for fetching the countries?


Solution

  • @for iterates over arrays. According to your types, countries$ is an Observable of a Country object, which Angular doesn't accept to iterate over. Just add [] to indicate that it's a list of Country objects:

    export class CountryComponent {
    
      countries$: Observable<Country[]>;
    
      constructor(private httpClient: HttpClient) {
          this.countries$ = this.httpClient.get<Country[]>('http://localhost:3000/countries');
      }
    }
    

    One additional note, calling response.json() is not something which Angular's HTTP client supports. It will parse to JSON by default, so probably you can just skip this part.