Home > OS >  How do you turn an Observable array of objects, into an Observable of the data inside that array of
How do you turn an Observable array of objects, into an Observable of the data inside that array of

Time:12-18

I'm getting this error:

Type 'Observable<Country[]>' is not assignable to type 'Observable'. Type 'Country[]' is missing the following properties from type 'Country': name, tld, alpha2Code, alpha3Code, and 20 more.ts(2322


I think it's because the endpoint im reaching serves back an array with an object inside of it. Here's an example.

https://restcountries.com/v3.1/name/peru


This is the code I can't get to work. It refuses to compile and gives me the error I posted above.

export class DetailsComponent implements OnInit {
  country$!: Observable<Country>
  countryBorders$!: Observable<Country[]>

  constructor(private apiService: ApiService, private activatedRoute: ActivatedRoute){}

  ngOnInit(): void {
    this.activatedRoute.params.subscribe((params) => {
      let countryName = params['country'];
      this.country$ = this.apiService.getCountryByName(countryName)
    });
    };
  }

The getCountryByName() method looks like this:

  getCountryByName(name: string) {
    return this.http
      .get<Country[]>(`${this.api}/name/${name}`)
  }

How do I get the "this.country$" variable/observable/subject (idk what it is technically), to hold the data thats inside the object thats inside of the array that's being returned by the HTTP request?

I thought I could map the values out of the array, but this doesn't work either:

  ngOnInit(): void {
    this.activatedRoute.params.subscribe((params) => {
      let countryName = params['country'];
      this.country$ = this.apiService.getCountryByName(countryName).pipe(map(([info])=>return info)
    });
    };
  }

When I do that, I don't get the error but this template:

<div *ngIf="country$ | async as country">
  <h1>{{country$}}</h1>
</div>

renders

[object Object]

...like the h1 on the page literally just says "[object Object]"


What am I doing wrong? What combination of operators do I need to use to convert the 'Observable<Country[]>' into just 'Observable<Country' so I can render it inside an html template like this?

  <div>
    <p><strong>Native Name: </strong>{{ country$.name.nativeName }}</p> 
    <p><strong>Population: </strong>{{ country$.population | number : '0.0' }}</p> 
    <p><strong>Region: </strong>{{ country$.region }}</p> 
    <p><strong>Sub Region: </strong>{{ country$.subregion }}</p> 
    <p><strong>Capital: </strong>{{ country$.capital }}</p></div>
    <div> 
      <p><strong>Top Level Domain: </strong>{{ country$.tld }}</p> 
      <p><strong>Currencies: </strong>{{ country$.currencies }}</p> 
      <p><strong>Languages: </strong>{{ country$.languages }}</p>
   </div>
</div>

This is the interface in case it's relevant to the answer:

export interface Country {
  name: Name; //---
  tld: string[]; //---
  cca2: string;
  ccn3: string;
  cca3: string;
  cioc: string;
  independant: Boolean
  status: string;
  unMember: Boolean;
  currencies: Currency[]; //---
  idd: Idd;
  capital: string[]; //---
  altSpellings: string[];
  region: string;
  subregion: string; //---
  languages: any; //---
  translations: any;
  latlng: number[];
  landlocked: Boolean;
  borders: string[]; //---
  area: number;
  demonyms: any;
  flag: string;
  maps: Maps;
  population: number; //---
  gini: any;
  fifa: string;
  car: any;
  timezones: string[];
  continents: string[];
  flags: Flags;
  coatOfArms: COA;
  startOfWeek: string;
  capitalInfo: Capital;
  postalCode: Postal;
  // nativeName: string;
  // numericCode: string;
  // regionalBlocs: RegionalBloc[];
}

CodePudding user response:

Looks like you have a typing issue in your getCountryByName method. Instead of .get<Country[]>, it should be .get<Country>:

getCountryByName(name: string) {
  return this.http
    .get<Country>(`${this.api}/name/${name}`)
}

CodePudding user response:

The RestCountries API returns the response with an array type. You can achieve with extract the first element of the array via map rxjs.

getCountryByName(name: string): Observable<Country> {
  return this.http
    .get<Country[]>(`${this.api}/name/${name}`)
    .pipe(map((x) => x[0]));
}

The way you extract the properties from the country$ Observable is incorrect. You should look for an async pipe as what you have achieved in the question.

<div *ngIf="country$ | async as country">
  <div>
    <p><strong>Native Name: </strong>{{ country.name.nativeName | json }}</p>
    <p>
      <strong>Population: </strong>{{ country.population | number: '0.0' }}
    </p>
    <p><strong>Region: </strong>{{ country.region }}</p>
    <p><strong>Sub Region: </strong>{{ country.subregion }}</p>
    <p><strong>Capital: </strong>{{ country.capital }}</p>
  </div>
  <div>
    <p><strong>Top Level Domain: </strong>{{ country.tld }}</p>
    <p><strong>Currencies: </strong>{{ country.currencies }}</p>
    <p><strong>Languages: </strong>{{ country.languages }}</p>
  </div>
</div>

Demo @ StackBlitz

  • Related