Processing a complex object by http get in Angular 6

Artemy Khodorev picture Artemy Khodorev · May 25, 2018 · Viewed 15.8k times · Source

I do not understand how to handle the object I subscribe to. The object is the following structure:

{
  data:{
       date: "2018-02-20 13:10:23",
       text: "Описание",
       id: 1,
       items: [
              0: {
                 date: "2018-02-20 13:10:23",
                 text: "Описание",
                 images: [
                         0: "image1.jpg",
                         1: "image2.jpg"
                         ],
                 name: "Изображения",
                 type: "images"
                 },
              1: {
                 date: "2018-02-20 13:10:23",
                 text: "Описание",
                 image: null,
                 type: "video",
                 url: "https://www.youtube.com/embed/v64KOxKVLVg"
                 }
              ]
       }
}

I make an appeal through the service:

import {HttpClient} from '@angular/common/http';
import {Injectable} from '@angular/core';
@Injectable()
export class VideoService {
    constructor(private http: HttpClient) {}

    getVideoTape() {
        return this.http.get(`http://ip_adress/api/v1/mixed_galleries/1`);
    }
}

There is an interface model:

export class VideoListModel {
    constructor(
        public created_at: string,
        public description: string,
        public id: number,
        public items: any[],
        public name: string
    ) {}
}

And I do the processing in the component:

import {Component, OnDestroy, OnInit} from '@angular/core';
import {Observable, Subscription} from 'rxjs';
import {filter} from 'rxjs/operators';
import {VideoService} from '../shared/services/video.service';
import {VideoListModel} from '../../shared/models/video-list.model';

@Component({
  selector: 'app-video-index',
  templateUrl: './video-index.component.html',
  styleUrls: ['./video-index.component.scss']
})

export class VideoIndexComponent implements OnInit, OnDestroy {
    private videoTape = [];
    private _subscription2: Subscription;

    constructor( private videoService: VideoService ) { }

  ngOnInit() {
      this._subscription2 = this.videoService.getVideoTape()
          .subscribe((data: VideoListModel[]) => {
          this.videoTape = data;
          console.log(this.videoTape);
      });
  }

    ngOnDestroy() {
        this._subscription2.unsubscribe();
    }

}

The task is to make a selection from objects by type: "video". Through AJAX + jQuery did it without problems, and in Angular I'm relatively new. Shoveled a bunch of video lessons yesterday, but nowhere are examples of filtering such complex objects.

Construction:

this._subscription2 = this.videoService.getVideoTape()
          .pipe(filter((data: VideoListModel[]) => data.items.type === 'video'))
          .subscribe((data: any) => {
              this.videoTape = data.data;
              console.log(this.videoTape);
          });

does not work. The result is an error saying "Property 'items' does not exist on type 'VideoListModel []'". Intuitively, I understand that the matter is most likely in the interface, but I can not understand how to modify the interface so that the filtering works correctly. If someone encountered filtering complex objects, tell me please how to solve this problem.

Answer

Felix Lemke picture Felix Lemke · May 25, 2018

You don't have an array of VideoModels but an array of items in an object. Piping the whole content to a filter lets you filter items out of arrays, but you have an object. You could try the following workaround:

Create an interface like this

interface Item {
  date: Date;
  text: string;
  images: string[];
  name: string;
  type: string;
}

export interface VideoModel {
  data: {
    date: Date;
    text: string;
    id: number;
    items: Item[];
  }
}

Then you can use HttpClient in your Service as follows

import { Observable } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
[...]

getVideoTape(): Observable<VideoModel> {
  return this.http.get<VideoModel>(url).pipe(
    map(model => {
      const items = model.data.items.filter(item => item.type === 'video');
      model.data.items = items;
      return model;
    }),
    catchError(error => console.error(error))
  );
}

Pay attention to your images array, as it isn't valid json, string[]? Wouldn't it be better to filter the types serverside in order to reduce traffic?