racinne
racinne

Reputation: 45

How to pass and change index of array in vue?

I have the gist of how to do this, but I'm a beginner in vue, and I'm struggling with how to put it together. I need Control.vue to update the index in Exhibitor.vue. I know I'll have an $emit event happening in Control when I click on the button to pass the index data to the parent, and I'd have to use props to pass data from Exhibitor to its children, but how? I can't understand how to pass the index of an array with my code.

Exhibitor.vue

<template>
  <div id="exhibitor">
  <section class="exhibitor_info">
    <h1 class="exhibitor_name">{{ exhibitors[index].firstName }} {{ exhibitors[index].lastName }}</h1>
    <h2>Tag Number: {{ exhibitors[index].tagNum }} <em>{{ exhibitors[index].species }}</em></h2>
  </section>
  <div class="frame"><img :src="getImgUrl(exhibitors[index].picture)" alt="Exhibitor-Picture" class="image"></div>
  </div>
</template>

<script>
export default {
  name: 'Exhibitor',
  data() {
    return {
    exhibitors: [],
    index: 0
    }
  },
  created: function() {
    this.fetchExhibitors();
  },
  methods: {
    fetchExhibitors() {
      let uri = 'http://localhost:8081/exhibitor'
      this.axios.get(uri).then(response => {
        this.exhibitors = response.data
      })
    },
    getImgUrl: function(pic) {
      return require('../assets/' + pic)
    }
  }
}
</script>

Display.vue

<template>
  <div id="display">
    <exhibitor></exhibitor>
    <buyer></buyer>
  </div>
</template>

<script>
import Exhibitor from './Exhibitor.vue';
import Buyer from './Buyer.vue';

export default {
  components: {
    'exhibitor': Exhibitor,
    'buyer': Buyer
  }
}
</script>

Control.vue

<template>
  <div id="control">
    <display></display>
      <button v-on:click="incrementLeft">Left</button>
      <button v-on:click="incrementRight">Right</button>
  </div>
</template>

<script>
import Exhibitor from './Exhibitor.vue';
import Display from './Display.vue';
export default{
  props: ['exhibitors', 'buyers', 'index'],
  data() {
    return {
      index: 0
    }
  },
  methods: {
    incrementRight: function() {
      // Note that '%' operator in JS is remainder and NOT modulo
      this.index = ++this.index % this.exhibitors.length
    },
    incrementLeft: function() {
      // Note that '%' operator in JS is remainder and NOT modulo
      if (this.index === 0) {
        this.index = this.exhibitors.length - 1
      } else {
        this.index = --this.index % this.exhibitors.length
      }
    }
  },
  components: {
    'display': Display
  }
}
</script>

Upvotes: 0

Views: 2932

Answers (1)

Andrew1325
Andrew1325

Reputation: 3579

So you can get what you want to happen and there are two ways of making it happen that I can think of. First I will just clarify the terms relating to this because you seem to have them the wrong way around. Let's look at you tier structure which is like this:

Control.vue
  contains: Display.vue
    contains: Exhibitors.vue & Buyers.vue.

Therefore Control.vue is the parent of Display.vue which is the parent of Buyers.vue and Exhibitors.vue.

Anyway, What we need to do is control the array of exhibitors (and I guess buyers but you didn't include them in your code so I'll do likewise) which is in Exhibitors.vue from Control.Vue even though they don't have a direct parent child relationship. What I've done is set a prop that is passed to Display.vue which uses scoped slots to render the exhibitors from Exhibitors.Vue. Because the left and right buttons need to know when to stop going I have emitted the array length from Exhibitors.vue to Display.vue and again to Control.vue. It all works so heres some code.

//Control.vue
<template>
    <div class="content">
        <display v-on:finalLength="setIndexLimit" :i="index"></display>
        <button @click="changeDown">Down</button>
        <button @click="changeUp">Up</button>
        <p>{{indLimit}}</p>
    </div>
</template>

<script>
import Display from '@/components/Display'
export default {
    components: {
        Display
    },
    data: () => ({
        index: 0,
        indLimit: 0
    }),
    methods: {
        changeUp() {
            if (this.indLimit === this.index+1) {
                this.index=0
            }
            else {
                this.index ++
            }
        },
        changeDown() {
            if (this.index === 0) {
                this.index = this.indLimit - 1
            }
            else {
                this.index --
            }
        },
        setIndexLimit(e) {
            this.indLimit = e
        }
    }
}
</script>

and

//Display.vue
<template>
  <div id="display">
      <p>From Display</p>
    <exhibitors v-on:ExLength="setLength">
        <p>{{i}}</p>
    </exhibitors>
    <exhibitors>
        <p slot-scope="{ exhibitors }">{{exhibitors[i].firstName}}</p>
    </exhibitors>
    <exhibitors>    
        <p slot-scope="{ exhibitors }">{{exhibitors[i].lastName}}</p>
    </exhibitors>
    <exhibitors> 
        <p slot-scope="{ exhibitors }">{{exhibitors[i].tagNum}}</p>
    </exhibitors>
    <exhibitors>     
        <p slot-scope="{ exhibitors }">{{exhibitors[i].species}}</p>
    </exhibitors>
  </div>
</template>

<script>
import Child from '@/components/admin/Exhibitors'

export default {
  components: {
    Exhibitors
  }, 
  props: [
      'i'
  ],
  data: () => ({
      exhibitLength: null
  }),
  methods: {
      setLength(e) {
          this.exhibitLength = e
          this.$emit('finalLength',e)
      }
  }

}
</script>

And finally:

//Exhibitors.vue
<template>
  <div>
    <slot :exhibitors="exhibitors"><p>I'm the child component!</p></slot>
  </div>
</template>

<script>
export default {
  data: () => ({
    exhibitors: [
            {
                firstName: 'Joe',
                lastName: 'Burns',
                tagNum: 1,
                species: 'ant'
            },
            {
                firstName: 'Tom',
                lastName: 'Yorke',
                tagNum: 2,
                species: 'bee'
            },
            {
                firstName: 'Flit',
                lastName: 'Argmeno',
                tagNum: 3,
                species: 'giraffe'
            }
        ],
  }),
  mounted: function () {
    this.$nextTick(function () {
        let length = this.exhibitors.length
        this.$emit('ExLength', length)
    })
  }
}
</script>

So as I said all that works, you can click the buttons and it will loop through the array contents. You can style it how you want wherever you like. However, it is a bit messy with props and slots and emits and whatnot just to relay a single index number and it would be far easier in my opinion to have a vuex store where 'index' is stored as a state item and can be used and changed anywhere without all the above carry on.

Upvotes: 0

Related Questions