What is the best way to showcase elements from an array of strings in the app only if they have a length greater than zero?

I'm currently developing a Single Page Application (SPA) using Vue 3, TypeScript, and The Movie Database (TMDB).

This application showcases a list of movie cards.

Within the MoviesList component (src\components\MoviesList.vue), the following code is implemented:

<template>
    <div class="row list">
       <div v-for="movie in movies" :key="movie.id" class="col-xs-12 col-sm-6 col-lg-4 col-xl-3">
        <MovieCard :movie="movie" :genres="genres" />
    </div> 
  </div>
</template>

<script lang="ts">
    import { defineComponent, ref } from 'vue';
    import axios from 'axios';
    import env from '../env';
    import MovieCard from './MovieCard.vue';

    export default defineComponent({
        name: 'MoviesList',
        components: {MovieCard},
        props: {
            listType: String
        },

    data() {
      return {
        pageTitle: "Now Playing",
        movies: [],
        genres: [],
      }
    },

    mounted() {
      this.getMovies();
      this.getGenres();
    },

     methods: {
      getMovies() {
        axios.get(`${env.api_url}/movie/${this.$props.listType}?api_key=${env.api_key}`).then(response => {
          this.movies = response.data.results;
        })
        .catch(err => console.log(err));
      },

      getGenres() {
        axios.get(`${env.api_url}/genre/movie/list?api_key=${env.api_key}`).then(response => {
          this.genres = response.data.genres;
          console.log(this.genres);
        })
        .catch(err => console.log(err));
      }
    }
  });
</script>

Within the MovieCard component (src\components\MovieCard.vue), the following code is implemented:

<template>
    <div class="movie card">
        <div class="thumbnail">
            <img :src="`https://image.tmdb.org/t/p/w500/${movie.backdrop_path}`" :alt="movie.title" class="img-fluid" />
        </div>

        <div class="card-content">
            <h2 class="card-title">{{ movie.title }}</h2>
            <p class="card-desc">{{ movie.overview }}</p>
            <span :title="`Score: ${movie.vote_average}`" class="score d-flex">{{ movie.vote_average }}</span>
        </div>

        <div class="card-footer">
            <p class="m-0 release">Release date: {{ dateTime(movie.release_date) }}</p>
            <p class="m-0 pt-1">
                <a class="genre" v-for="genre in movie.genre_ids" :key="genre.id">
                    {{ getGenreName(genre) }}
                </a>
            </p>
        </div>
    </div>
</template>

<script lang="ts">
  import { defineComponent } from 'vue';
  import moment from 'moment';

  export default defineComponent({
    name: 'MovieCard',

    props: {
        movie: {
            type: Object,
            required: true
        },

        genres: {
            type: Object,
            required: true
        }
    },
    
  methods: {
    dateTime(value: any) {
            return moment(value).format('MMMM DD, YYYY');
        },

        getGenreName(gid: number) {
            if (this.genres[gid]) {
                if (this.genres[gid].name.length) {
                    return this.genres[gid].name;
                }
            }
        }
    },
  });
</script>

The Issue

To avoid displaying empty genre containers, I have utilized the following condition:

if (this.genres[gid].name.length) {
    return this.genres[gid].name;
}

However, for reasons unknown to me, this approach is failing as shown below.

https://i.sstatic.net/Fiiiyl.jpg

Queries

  1. Where did I go wrong?
  2. What is the most foolproof method to achieve the desired outcome?

Answer №1

One problem arises when the key :key="genre.id" is used. The

v-for="genre in movie.genre_ids"
indicates that genre is likely an ID (number). As a result, genre.id may return undefined, causing each iteration to have the same key. This could lead to rendering issues.


Additionally, the presence of empty tags suggests that there is no return value for {{ getGenreName(genre) }}.

Several if-statements have been included in an attempt to address this issue, but if a genre ID is not found in this.genres, the function will return

undefined</code. The same applies if the genre name is empty. Instead of returning an empty string, it returns <code>undefined
, resulting in the same outcome.

To resolve this, let's first ask whether all genres have been loaded. If a movie genre ID is absent from this.genres, you may need to make multiple calls to obtain a complete list of genres due to API pagination.

If you are certain that all genres have been loaded and there are still mismatched genre IDs or genres with empty names, a computed property can be utilized to tackle the issue.

computed: {
  movieGenres() {
    return this.movie.genre_ids
      .filter(genre_id => genre_id in this.genres)
      .map(genre_id => this.genres[genre_id])
      .filter(genre => genre.name.length > 0);
  }
}

Now, instead of iterating through movie.genre_ids, you can iterate through movieGenres instead.

<a class="genre" v-for="genre in movieGenres" :key="genre.id">
  {{ genre.name }}
</a>

It should be noted that in the above code snippet, genre represents the actual genre object rather than just an ID. This transformation occurs thanks to the

.map(genre_id => this.genres[genre_id])
performed within the computed property.


An alternative to using a computed property is to enhance data delivery. Instead of passing "invalid" data as a property, consider filtering out such data upon receiving a response from the API.

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

Exploring the world of Vue and Nuxt: delving into the depths

For years, I've been using Vue as a client-side library without delving into server-side rendering. Recently, I came across some tutorials on the topic and noticed a difference between normal client-side fetching with the mount() hook. <template> ...

The $index can be effectively utilized to alternate the click event of elements nested within the same parent element

I'm currently utilizing ng-repeat to exhibit a multidimensional array... <div class="form-container" ng-repeat="formblock in forms"> <div ng-click="showResults($index)" ng-if="repeat == true" class="drop" type="button">{{ formblock[0] ...

Dynamic autocomplete feature with AJAX integration for filtering in Flask

Looking for some guidance on creating an HTML form with two input fields. Check out the HTML template code below: <form role="form" action="/cities/" method="get" autocomplete="on"> <label for="#input1"><strong>Country:</strong&g ...

Disabling a button after clicking using jQuery

There are occasions when I can inadvertently trigger the submit button twice, resulting in the ajax being triggered twice as well. Below is my HTML code: <div class="buttons-area"> <input id="step-two" class="btn btn-primary" type="submit" v ...

The parameter provided should be in the form of a 12-byte string

Hey there, I am facing an issue while trying to delete an entry in my database. Despite attempting JSON.parse on the req.body and rearranging the routes in my routes file, I still can't seem to get it to work. Here is my controller: async function re ...

Vue nearly completed production build displays a message saying "This webpage is causing your browser to lag"

I'm currently in the process of developing a web application using Vue and Quasar. In my development environment, everything functions properly and loads as expected. I start the development server like so: npm run serve However, when I attempt to bu ...

Issue with Google Adsense - adsbygoogle.push() error: Pages can only support one AdSense head tag. The additional tag will be disregarded

After adding my script tag to the navbar component, I encountered an error on my site during the next js build: Google Adsense error -adsbygoogle.push() error: Only one AdSense head tag supported per page. The second tag is ignored <Head> ...

Tips for removing a checkbox and customizing a label's style when the label wraps around the checkbox input

Hello, I'm an advanced beginner so I appreciate your patience:) I've had success removing checkboxes and styling their labels when the for="" attribute is present. However, I'm facing a challenge with a form where the labels wrap the input ...

innerHTML not showing up on the page

Trying to implement a dynamic navbar that changes based on whether a user is signed in or not. The authentication part is functioning correctly (verified with console logs); however, there seems to be an issue with updating the HTML using .innerHTML = ... ...

Setting focus on an input box using programmatic methods triggered by the ItemInvoked event in a ListView in a Windows 8 Metro HTML5 environment

Note: If you have not worked on Windows 8 before, please refrain from answering this question. You are advised not to vote or read it unless you are familiar with Windows 8 METRO HTML5/js applications that run in the Windows Runtime environment. Query: I ...

Exceeded maximum file size event in Dropzone

I am currently implementing dropzone in my form to allow users to upload images. In the event that a user selects a file that exceeds the configured limit, I want to display an alert message and remove the file. Below is my current configuration: Dropzone ...

Utilizing an external script by importing it with a script tag and integrating its functions into a React component

I am currently using the teller.io API in my application. To initiate authentication within my react app, I need to import a script from them since there is no react library available. Once the script is added to the DOM, I need to use a function from it. ...

Updating data with Sequelize

I've been grappling with this issue for a day now and can't seem to find a solution. This is the code snippet I am currently using On the client side: const nameInput = document.querySelector("#nameInput"); const urlInput = document.qu ...

The proper way to utilize the hook functionality from the composition API in Vue 3

Exploring the new composition api in Vue 3 has been an interesting journey for me. I recently learned how to make API calls using the composition api, but unfortunately, I am facing some difficulties with integrating it into my code. Even though the API ca ...

Using NextJS: Customizing page names for cleaner URLs

Currently working on a project in NextJS, I've come across an interesting observation. The file name within my pages folder seems to directly correspond to the path displayed in the URL bar. My question is, can I possibly create a structure like this ...

Updating the color of text when clicking on a link using javascript

I am facing an issue where I cannot seem to change the text color using JavaScript. The functionality to show and hide div tags is working perfectly, but changing the color seems to be a challenge. It's worth noting that there are no styles defined fo ...

Is it possible to retract a message on Discord after it has been sent?

I have a code that automatically sends a welcome message when a new member joins the guild, and I want it to be deleted shortly afterwards. Here is my current code: client.on('guildMemberAdd', (member) => { var server = member.guild.id; i ...

What is causing me to encounter an additional hour when computing time differences in JavaScript?

I am attempting to create a simple time difference calculation by subtracting the end time from the start time. However, I am noticing that I am getting an extra hour in my result. I believe this may be due to my timezone being GMT+1. Regardless of the ti ...

Is it necessary to publish a package for client-side usage on npm?

I'm struggling to understand the recent trend of using npm to publish client-side packages that have no dependencies. Take for example a simple class that extends HTMLElement and can only be used in the browser by adding a script tag to an HTML file. ...

The use of "app.use("*") appears to be triggering the function repeatedly

app.use("*", topUsers) is being invoked multiple times. The function topUsers is called repeatedly. function topUsers(req, res, next){ console.log("req.url", req.url) findMostUsefullReviewsAggregate(6) .then(function(aggregationResult){ ...