Matching genre ids with genre names in TMDb with JavaScript (Ember.js)

若如初见. 提交于 2019-12-11 17:43:44

问题


I'm sure a lot of you have used the TMDb (The Movie Database) api for movies. But I'm having issues with showing the genre names for each movie displayed. I'm trying to replace each number in genre_ids, from movies api, with their corresponding name from genres api, as showing numbers to users doesn't say much! But I don't get the desired result. I'm not sure what the correct way is...

Movie adapter

import DS from 'ember-data';

const apiKey = 'SOME_API_KEY_HERE';

export default DS.RESTAdapter.extend({
  host: `https://api.themoviedb.org/`,
  namespace: '3',
  pathForType() {
    return `discover/movie?sort_by=popularity.desc&api_key=${apiKey}`;
  },
});

Genre adapter

import DS from 'ember-data';

const apiKey = 'SOME_API_KEY_HERE';

export default DS.RESTAdapter.extend({
  host: `https://api.themoviedb.org/`,
  namespace: '3',
  pathForType() {
    return `genre/movie/list?api_key=${apiKey}`;
  },
});

Movie serializer

import DS from 'ember-data';

export default DS.RESTSerializer.extend({
  normalizeResponse(store, primaryModelClass, payload, id, requestType) {
    payload = { movies: payload.results };
    return this._super(store, primaryModelClass, payload, id, requestType);
  }
});

Genre serializer

import DS from 'ember-data';

export default DS.RESTSerializer.extend({
  normalizeResponse(store, primaryModelClass, payload, id, requestType) {
    payload = { genres: payload.genres };
    return this._super(...arguments);
  }
});

Movie model

import DS from 'ember-data';

const { attr, hasMany } = DS;

export default DS.Model.extend({
  vote_count: attr('number'),
  video: attr('boolean'),
  vote_average: attr('number'),
  title: attr('string'),
  popularity: attr('number'),
  poster_path: attr('string'),
  original_language: attr('string'),
  original_title: attr('string'),
  genre_ids: attr(),
  backdrop_path: attr('string'),
  adult: attr('boolean'),
  overview: attr('string'),
  release_date: attr('date'),
});

Genre model

import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr('string'),
});

Route

import Route from '@ember/routing/route';
import RSVP from 'rsvp'

export default Route.extend({
  model() {
    return RSVP.hash({
      movies: this.store.findAll('movie'),
      genres: this.store.findAll('genre'),
    });
  },
});

Movie-listing Component

import Component from '@ember/component';
import { computed } from '@ember/object';

export default Component.extend({
  movieGenreIds: computed('movies.@each.genre_ids', function() {
    return this.movies.map(movie => movie.genre_ids).reduce((a, b) => [...a, ...b]);
  }),

  genresNames: computed('movieGenreIds', 'genres', 'movies', function() {
    let names = [];

    this.genres.map((genre) => {
      this.movieGenreIds.forEach(movieGenreId => {

        if (parseInt(genre.id) === movieGenreId) {
          names.push(genre.name);
        }
      })
    })

    return names;
  }),
});

Movies API (each movie from the results array has this structure):

{
  "vote_count": 1092,
  "id":335983,
  "video": false,
  "vote_average": 6.7,
  "title": "Venom",
  "popularity": 505.173,
  "poster_path": "\/2uNW4WbgBXL25BAbXGLnLqX71Sw.jpg",
  "original_language": "en",
  "original_title": "Venom",
  "genre_ids": [27,878,28,53,35], // <-- I'm interested in this property
  "backdrop_path": "\/VuukZLgaCrho2Ar8Scl9HtV3yD.jpg",
  "adult": false,
  "overview": "When Eddie Brock acquires the powers of a symbiote, he will have to release his alter-ego “Venom” to save his life.",
  "release_date": "2018-10-03"
}

Genres API

"genres":[
  {"id":28,"name":"Action"},
  {"id":12,"name":"Adventure"},
  {"id":16,"name":"Animation"},
  {"id":35,"name":"Comedy"},
  ...
]

Hbs Template (the expected result)

<ul class="movie">
{{#each movies as |movie|}}
  <li>
    <h2 class="movie__title">{{movie.title}}</h2>
    <p class="movie__genre">
      genres: 
      {{#each genresNames as |genre|}}
        {{genre}} <!-- a list of genre names for this particular movie -->
      {{/each}}
    </p>
    <img src="https://image.tmdb.org/t/p/w500/{{movie.poster_path}}" alt="" class="movie__image">
  </li>
{{/each}}


回答1:


I think your primary problem is that you're trying to fix something on the component layer that is better handled on the model layer. While you can do that, what you actually want is a relationship from the movie model to the genre model:

genres: hasMany('genre'),

I'm not sure what your API provides 1:1 because you've not pasted the exact response. At some point you've mentioned a results array, and the genres seems to be wrapped inside a genres array. So if thats not 100% correct you maybe need to tweak this solution a bit.

For the start I would recommend the newer JSONSerializer instead of the RESTSerializer.

Now you need to tell ember that for the genres relationship it should use the ids provided in the genre_ids array. This can be done by keyForRelationship:

import DS from 'ember-data';
import {singularize} from 'ember-inflector';

export default DS.JSONSerializer.extend({
  ...
  keyForRelationship(key, typeClass, method) {
    return `${singularize(key)}_ids`;
  },
});

Here I use the ember-inflector to get the singular of the relationship name (so genres -> genre) and then just add _ids. This is enough for ember to recognize the ids and then use them to provide the right model instances.

Next you can basically just loop over genres on your movie model:

{{#each movie.genres as |genre|}}
  {{genre.name}}
{{/each}}

Now you don't even need to pass the list of all genres to the controller/template. However you still need to load them so ember-data can use them. Otherwise ember-data would try to fetch them individually when you use them.

So your model hook could look like this:

model() {
  return RSVP.hash({
    genres: this.store.findAll('genre'),
    movies: this.store.findAll('movie'),
  }).then(x => x.movies);
}

Here is a twiddle implementing this. However because I don't wanted to live-fetch the data I've created dummy adapters that return static data.



来源:https://stackoverflow.com/questions/52832701/matching-genre-ids-with-genre-names-in-tmdb-with-javascript-ember-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!