Невозможно привязать данные к таблице данных материалов Angular

Проблема

У меня проблема с привязкой данных JSON к таблице данных Angular Material.

Фон

Я разрабатываю небольшое приложение, которое предназначено для предоставления пользователям информации о фильмах. Вызов API инициируется после того, как пользователь запускает поиск. Затем это должно заполнить таблицу данных данными ответа.

Хотя я могу успешно выполнить вызов API, данные в таблицу данных не передаются:

Ответ API в инструментах разработчика

Кроме того, в консоли не отображаются ошибки, и я могу заполнить таблицу тестовыми данными.

Вот код:

api-calls.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';

@Injectable()
export class ApiService {
constructor(private https:HttpClient){}

public getFilms(searchTerm): Observable<any> {
  const apiUrl = 'https://www.omdbapi.com/?apikey=b1464edd&s=';
  const fullLink = apiUrl + searchTerm
  return this.http.get(fullLink)
}}

app.component.ts

import { Component, OnInit } from '@angular/core';
import { Films } from './models/films.model';
import { ApiService } from './services/api-calls.service';
import { MatTableDataSource } from '@angular/material';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent implements OnInit{

constructor(private apiService:ApiService) {}

displayedColumns: string[] = ['title', 'year', 'imdbID', 'poster', 'type']
dataSource: MatTableDataSource<any[]>;
searchTerm = '';

handleSearch(event) {
  if(event.action === 'SEARCH') {
    this.searchTerm = event.query
    this.apiService.getFilms(this.searchTerm).subscribe(
      data => this.dataSource = new MatTableDataSource<Films[]>(data)
    )}
  }

  ngOnInit() {
  }
}

app.component.html (функция поиска, описанная в классе контейнера, обрабатывается в другом компоненте)

<div class = "container">
      <mat-card>
          <h2>Lightweight IMDb Search Engine</h2>
          <app-search (searchEvent)="handleSearch($event)"></app-search>
      </mat-card>

      <div>
        <mat-table [dataSource] = "dataSource">
          <ng-container matColumnDef="title">
            <mat-header-cell *matHeaderCellDef> Title </mat-header-cell>
            <mat-cell *matCellDef="let films"> {{ films.title }}</mat-cell>
          </ng-container>
          <ng-container matColumnDef="year">
            <mat-header-cell *matHeaderCellDef> Year </mat-header-cell>
            <mat-cell *matCellDef="let films"> {{ films.year }}</mat-cell>
          </ng-container>
          <ng-container matColumnDef="imdbID">
            <mat-header-cell *matHeaderCellDef> imdbID </mat-header-cell>
            <mat-cell *matCellDef="let films"> {{ films.imdbID }}</mat-cell>
          </ng-container>
          <ng-container matColumnDef="poster">
            <mat-header-cell *matHeaderCellDef> Poster </mat-header-cell>
            <mat-cell *matCellDef="let films"> {{ films.poster }}</mat-cell>
          </ng-container>
          <ng-container matColumnDef="type">
            <mat-header-cell *matHeaderCellDef> Type </mat-header-cell>
            <mat-cell *matCellDef="let films"> {{ films.type }}</mat-cell>
          </ng-container>
          <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
          <mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
        </mat-table>
      </div>
  </div>

film.model.ts

export interface Films {
    title: string;
    year: string;
    imdbID: string;
    poster: string;
    type: string;
}

Подтвердите, что данные, которые возвращаются, имеют ожидаемую форму и не вложены или что-то в этом роде   —  person RuffMingani    schedule 08.03.2020

См. также:  Угловые советы | Используйте технику проекции для улучшения возможности повторного использования ваших компонентов ?

Интересно, что с кодом, который я предоставил, когда я пробую console.log (data) или console.log (dataSource), он возвращается с undefined. Попробую получить запись данных в консоль.   —  person RuffMingani    schedule 08.03.2020

Значит, проблема в форме мероприятия. Оцените это: omdbapi.com/?apikey=b1464edd&s=hello обратите внимание на ответ фильмы вложены в свойство поиска, поэтому вам также нужно будет добавить другую услугу, например return this.http.get(fullLink).pipe(map(r => r.Search))   —  person RuffMingani    schedule 08.03.2020

Добавьте код для app-search   —  person RuffMingani    schedule 08.03.2020

Спасибо, return this.http.get(fullLink).pipe(map(r => r.Search)). Кроме того, мне потребовалось изменить film.model.ts из-за чувствительности к регистру.   —  person RuffMingani    schedule 08.03.2020

Теперь он работает?   —  person RuffMingani    schedule 08.03.2020

Это да! Извините, вчера я не закончила фразу. Спасибо за твою помощь.   —  person RuffMingani    schedule 09.03.2020

Понравилась статья? Поделиться с друзьями:
IT Шеф
Комментарии: 1
  1. RuffMingani

    JSON, возвращаемый API, имеет следующую форму

    {
      "Search": [
        {"Title":"Hello, My Name Is Doris","Year":"2015","imdbID":"tt3766394","Type":"movie","Poster":"https://m.media-amazon.com/images/M/[email protected]_V1_SX300.jpg"},
        // etc.
      ]
    } 
    

    Поэтому вам необходимо внести некоторые коррективы.

    Во-первых, вам нужно спроецировать ответ, извлекая свойство Search, которое содержит массив фильмов. Это должно быть сделано в вашей службе (обратите внимание на улучшенное использование типов)

    api-calls.service.ts

    import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { Observable } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    import Film from './models/film.model';
    
    @Injectable()
    export class ApiService {
      constructor(private https:HttpClient){}
    
      getFilms(searchTerm): Observable<Film[]> {
        const apiUrl = 'https://www.omdbapi.com/?apikey=b1464edd&s=';
        const fullLink = apiUrl + searchTerm;
        type Response = { Search: Film[] };
        return this.http.get<Response> (fullLink)
          .pipe(map(response => response.Search));
      }
    }
    

    Затем нам нужно объявить имена свойств в интерфейсе модели, чтобы правильно описать форму пленок в ответе.

    film.model.ts

    export default interface Film {
        Title: string;
        Year: string;
        imdbID: string;
        Poster: string;
        Type: string;
    }
    

    Теперь давайте настроим сам компонент, чтобы немного улучшить типы

    app.component.ts

    import { Component } from '@angular/core';
    import { ApiService } from './services/api-calls.service';
    import { MatTableDataSource } from '@angular/material';
    
    import Film from './models/film.model';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    
    export class AppComponent {
    
      constructor(private apiService:ApiService) {}
    
      displayedColumns: (keyof Film)[] = ['Title', 'Year', 'imdbID', 'Poster', 'Type'];
      dataSource?: MatTableDataSource<Film[]>;
      searchTerm = '';
    
      handleSearch({ action, query }) {
        if (action === 'SEARCH' && query) {
          this.searchTerm = query;
          this.apiService.getFilms(this.searchTerm)
            .subscribe(films => this.dataSource = new MatTableDataSource(films));
        }
      }
    }
    

    Обратите внимание, как было улучшено использование типов, чтобы ограничить имена столбцов, чтобы они оставались синхронизированными между компонентом и службой. Также обратите внимание, что избыточная информация о типе была улучшена, чтобы использовать преимущества вывода типа, вытекающего из улучшенной сигнатуры типа метода службы.

Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: