Pairing TMDb genre IDs and their respective names using JavaScript within the Ember.js framework

If you've ever worked with the TMDb (The Movie Database) api for movies, you might have encountered this issue. I am struggling to display the genre names for each movie shown. My goal is to replace the numbers in genre_ids from the movies api with their respective name from the genres api because displaying numbers to users doesn't provide much information! However, I haven't been able to achieve the desired outcome and I'm not certain of the correct approach...

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('<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ff929089969a8cd1bf9a9e9c97d1989a918d9a">[email protected]</a>_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}}

Answer №1

It seems like the primary issue you're facing is attempting to resolve a problem at the component layer that would be better addressed at the model layer. While it is possible to do so, what you truly need is a relationship from the movie model to the genre model:

genres: hasMany('genre'),

Without the exact response details from your API, it's hard to determine if a 1:1 mapping is provided. You mentioned an array named `results`, with genres potentially nested inside another `genres` array. If this isn't completely accurate, some adjustments may be necessary for this solution to work effectively.

Initially, I recommend utilizing the newer JSONSerializer instead of the RESTSerializer.

To inform Ember that the genres relationship should use the IDs within the genre_ids array, you can utilize the keyForRelationship method:

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

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

In this implementation, the singularized version of the relationship name is obtained using ember-inflector (e.g., genres -> genre), followed by appending _ids. This is sufficient for Ember to recognize the IDs and associate them with the correct model instances.

Subsequently, you can iterate through the genres on your movie model:

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

This eliminates the need to pass the complete list of genres to the controller/template. However, the genres still need to be loaded so that ember-data can access them. Otherwise, ember-data will attempt to fetch them individually when required.

Your model hook could resemble the following structure:

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

For implementing this approach, refer to this twiddle. As live data fetching was not needed, dummy adapters were created to provide static data.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Assistance needed for jQuery functions running too early

Within my click function, I am setting certain flags and then calling two functions. However, I want these two functions to execute sequentially in order after the flags have been set. jQuery("#element").click(function(e){ var op = jQuery("#box" ...

Is it possible to dynamically create input fields in AngularJS by clicking a button?

I'm currently working on a project that involves adding an input field and a button each time a user clicks on the button. Is there a way to retrieve the text from the input field when the new button, which is displayed alongside the input field, is ...

What is the best way to fetch values from individual buttons using PHP?

<form action="posts-ayarlar.php" method="POST" id="demo-form2" data-parsley-validate class="form-horizontal form-label-left"> <table class="table table-striped table-bordered" ...

Unable to click on link with JavaScript using Selenium webdriver

<a id="compareCompanies" b:onclick="needsController.showQuotes = true;" href="#">Compare companies</a> Below is the Selenium Webdriver JavaScript code using Mocha: driver.wait(function () { driver.findElement(webdriver.By.id("compareCompa ...

Error in code - message gets sent immediately instead of waiting for timeout to occur

There seems to be an issue with the code where the message is sent instantly instead of waiting for the specified timeout duration. Based on the code, it should wait for the time mentioned in the message. I'm puzzled as to why it's not functioni ...

What are the reasons behind the jQuery file upload failing to work after the initial upload?

I am currently utilizing the jQuery File Upload plugin. To achieve this, I have hidden the file input and set it to activate upon clicking a separate button. You can view an example of this setup on this fiddle. Here is the HTML code snippet: <div> ...

Is it possible to simultaneously use two $scoped variables within an Angular controller?

Currently, I am developing an angular application that connects to a Rails backend and interacts with the database through API calls to receive JSON objects. My challenge lies in defining multiple scoped variables within a controller. At the moment, I have ...

Gulp does not work well with Android APK compilation

Greetings, I am facing an issue while trying to compile my Android app using gulp with the command: gulp --prod -p android. The problem arises when comparing the file size of the generated APK between myself and a colleague. When my colleague compiles, the ...

Animating the smooth collapse of panels within listviews

I have successfully implemented a smooth animation code for a collapsible panel, and it is working wonderfully: <script type="text/javascript"> function pageLoad(sender, args) { smoothAnimation(); } function smoothAnimation() ...

Steps to dynamically display or conceal a DIV using Bootstrap 5

I am facing an issue with a navbar that has buttons to reveal hidden divs underneath. <a data-bs-toggle="offcanvas" href="#select" role="button" aria-controls="select"></a> <div id="select" c ...

What is the best way to retrieve the file name from the current document's URL using JavaScript?

I need help with a Javascript function that can extract the current file name without any parameters. $(location).attr('href').match(/([a-zA-Z\-\_0-9]+\.\w+)$/); var current_path = RegExp.$1; if ((current_path == 'index. ...

What could be the reason for my regex succeeding on the client side but failing on the server side

I have implemented a regex pattern to validate usernames, ensuring they only contain English letters, numbers, and underscores. The client-side code works perfectly, preventing any input other than these characters: <input type="text" name ...

Using AJAX to Send Requests to PHP

Embarking on my first ajax project, I believe I am close to resolving an issue but require some guidance. The webpage file below features an input field where users can enter their email address. Upon submission, the ajax doWork() function should trigger t ...

What is the process for separating static methods into their own file and properly exporting them using ES6?

After exploring how to split up class files when instance and static methods become too large, a question was raised on Stack Overflow. The focus shifted to finding solutions for static factory functions as well. The original inquiry provided a workaround ...

Refresh a Particular Section of a Website Without Having to Reload the Entire Page

I have this PHP script that I'm using to read specific phrases from a text file and then display one of them randomly on a webpage. Currently, the only way to see a new random phrase is by refreshing the entire page. I'm wondering if there is a w ...

Guide on sending a JavaScript function to a Pug file using Node.js and Express

In my recent project, I developed a node app using express along with a pug file. The express app is configured to listen on port 3000 and render the pug file. One of the key functionalities involves fetching information from an external API. My goal is to ...

Leveraging $http or $timeout in conjunction with $stateProvider in AngularJS

I am seeking guidance on loading a template for a specific state in Angular using $http after coming across this question on Stack Overflow: Is it possible to load a template via AJAX request for UI-Router in Angular? The documentation for ui.router demon ...

Is there a way to deactivate the previous button for today's date?

I need assistance with disabling the previous button on today's date in my calendar. It is crucial that users are only able to select the start date from today onwards. I am currently utilizing moment.js for my Datepicker. Any help would be greatly ap ...

Internal server error frequently occurs when there is an issue with Ajax requests in a Laravel application

Greetings, fellow developers! I am encountering an issue with the comments system in Laravel and Ajax. While it functions correctly with PHP alone, I am facing difficulties when using Ajax. The error message I am receiving is as follows: Status Code:50 ...

What is the method for getting js_xlsx to include all empty headers while saving the file?

In the midst of developing a Meteor App, I've incorporated the Node.js package known as "js_xlsx" from "SheetJS", produced by "SheetJSDev". This tool enables me to convert an Excel sheet uploaded into JSON on the backend. The intention is to store thi ...