Angular http.get() returns array with data

62 Views Asked by At

I have a service:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { Color } from '../model/color';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class ColorService {
  private productsUrl = 'http://localhost:5500/colors/';

  constructor(private http: HttpClient) { }

  getColors(): Observable<Color[]> {
    var colors = this.http.get<Color[]>(this.productsUrl + "all");
    
    return colors;
  }
}

and a component that gets the result of this method:

ngOnInit(): void {
  this.colorsService.getColors()
      .subscribe((colors) => {
        this.colors = Object.values(colors);
        this.colors.sort((a,b) => a.name.localeCompare(b.name));
        console.log(colors);
      }, err => {
        this.displaySpinner = false;
      }, () => {
        this.displaySpinner = false;
      });
}

My Color interface looks like this:

export interface Color {
    id: number;
    number: string;
    name: string;
    imagePath: string;
    category: string;
    color: string;
    brand: string;
}

Now when I do this console.log in the component, it gives me an array named colors, with as a child another array with the actual data:

{
  colors: [
    {
      id: 1,
      number: 159,
      name: 'glamorous peach',
      imagePath: '/src/assets/img/colors/color104.jpeg',
      category: 'summer',
      color: 'orange',
      brand: 'Pink Gellac'
    }
  ]
}

I seem to recall that this was not the case in a previous version of angular, and when I do something like the below in the HTML page, it is unable to get the color.name, I think because of the array nesting happening here:

<mat-grid-tile *ngFor="let color of colors">
    <mat-card class="example-card"
        style="width: 95%; border-radius:15px; box-shadow: 4px 4px 6px -1px rgba(0,0,0,0.15);">
        <mat-card-content>
            <mat-grid-list cols="1" rowHeight="250">
                <mat-grid-tile class="tile" style="overflow:auto;">
                  <div class="wrapper">
                    <p>{{color.name}}</p>
                  </div>
                </mat-grid-tile>
              </mat-grid-list>
        </mat-card-content>
    </mat-card>
</mat-grid-tile>

Any idea of how to work around this? Because of the typing in the service (Color[]), I seem to be unable to manipulate this array into what I need for the ngFor to work.

3

There are 3 best solutions below

1
Naren Murali On BEST ANSWER

The structure is coming from the API, you just need to set it differently for it to work! Access the colors array and set it to the variable!

ngOnInit(): void {
    this.colorsService.getColors()
        .subscribe((response: any) => {
          console.log(response);
          this.colors = <Array<Color>>response.colors;
          this.colors.sort((a,b) => a.name.localeCompare(b.name));
        }, err => {
          this.displaySpinner = false;
        }, () => {
          this.displaySpinner = false;
        });
  }
2
Kareem Adel On

you can modify your component code to extract the colors array from the response before assigning it to this.colors.

ngOnInit(): void {
  this.colorsService.getColors()
    .subscribe((response) => {
      // Extract the colors array from the response
      const colorsArray = response.colors;

      // Sort and assign the colors array to this.colors
      this.colors = colorsArray.sort((a, b) => a.name.localeCompare(b.name));

      console.log(this.colors);
    }, err => {
      this.displaySpinner = false;
    }, () => {
      this.displaySpinner = false;
    });
}

<!--  HTML template, you can iterate over this.colors directly: -->
<mat-grid-tile *ngFor="let color of colors">
  <mat-card class="example-card" style="width: 95%; border-radius:15px; box-shadow: 4px 4px 6px -1px rgba(0,0,0,0.15);">
    <mat-card-content>
      <mat-grid-list cols="1" rowHeight="250">
        <mat-grid-tile class="tile" style="overflow:auto;">
          <div class="wrapper">
            <p>{{color.name}}</p>
          </div>
        </mat-grid-tile>
      </mat-grid-list>
    </mat-card-content>
  </mat-card>
</mat-grid-tile>
0
Yong Shun On

Alternatively, you can transform the Observable value to retrieve the colors array via map RxJS operator.

import { map, Observable } from 'rxjs';


@Injectable({
  providedIn: 'root',
})
export class ColorService {
  ...

  getColors(): Observable<Color[]> {
    return this.http
      .get<any>(this.productsUrl + 'all')
      .pipe(map((resp: any) => resp.colors as Color[]));
  }
}

Demo @ StackBlitz