Line in Linus
Line in Linus

Reputation: 420

Vue Show more in deep array

I am trying to add a "show-more" button to an array(v-for) of an array (v-for).

<li v-for="(recruiter, key) in recruiters.data" :key="recruiter.id">
  <div v-if="recruiter.cities" >
    <div v-if="recruiter.cities.length <= 5 || recruiter.id in cities_expand">
      <a :href="'/rekryterare/' + city.slug" v-for="(city, index) in recruiter.cities">{{ city.name }}</a>
    </div>
    <div v-else>
      <template v-for="(city, index) in recruiter.cities">
        <template v-if="index+1 <= 4">
          <a :href="'/rekryterare/sverige/' + city.slug" >{{ city.name }}</a>
        </template>
      </template>
      <a href="#" @click.prevent="cities_expand.push(recruiter.id)" >...</a>
    </div>
  </div>
</li>

Basically if there is more than 4 items in recruiter.cities -> show 4 items + "show more"

However - the recruiter.id in cities_expand does not work/update?

I'm sure there is a better way of solving this - but nothing else came to mind :) Any ideas?

Upvotes: 0

Views: 273

Answers (1)

logee
logee

Reputation: 5077

I'm suspect that the problem is the use of the js in operator. The in operator checks if an object has a key in the object. It does not check if an array has a value.

So when doing recruiter.id in cities_expand, this checks if recruiter.id is a key in the array cities_expand. The keys of an array are the array indices.

This would explain why spam clicking will work because if the recruiter.id is e.g. 9, then recruiter.id in cities_expand will only be true if cities_expand has key 9 i.e. length 10. Which means you would have to click ... 10 times.

Changing

<div v-if="recruiter.cities.length <= 5 || recruiter.id in cities_expand">

to

<div v-if="recruiter.cities.length <= 5 || cities_expand.indexOf(recruiter.id) > -1 ">

should do the trick.


In terms of an alternative solution, I would encapsulate the inner loop in a component and use a computed value for the list. E.g.

Component RecruiterCities:

<template>
  <div v-if="displayCities" >
    <a :href="'/rekryterare/' + city.slug" v-for="(city, index) in displayCities" :key="index">{{ city.name }}</a>
    <a v-if="!showExpanded && totalCities > maxDisplayCities" href="#" @click.prevent="showExpanded = true" >...</a>
  </div>
</template>

<script>
export default {
  props: {
    recruiterCities: {
      type: Array,
      default() {
        return []
      }
    }
  },
  data() {
    return {
      showExpanded: false,
      maxDisplayCities: 4
    }
  },
  computed: {
    totalCities() {
      return (this.recruiterCities && this.recruiterCities.length) || 0;
    },
    displayCities() {
      if (!this.recruiterCities) {
        return [];
      }

      if (this.showExpanded || this.recruiterCities.length <= this.maxDisplayCities) {
        return this.recruiterCities;
      }

      return this.recruiterCities.slice(0, this.maxDisplayCities);
    }
  }
}
</script>

and to use it:

  <li v-for="(recruiter, key) in recruiters.data" :key="recruiter.id">
    <RecruiterCities :recruiter-cities="recruiter.cities"/>
  </li>

Upvotes: 1

Related Questions