Count the occurrences of a child component

血红的双手。 提交于 2021-01-28 10:56:39

问题


I have a single file component like this:

<template>
    <div>
        <template v-if="offers.length > 3">
            <a href="#">View all offers here</a>
        </template>

        <template v-else-if="offers.length > 1">
            <offer v-for="offer in offers" :data="offer"></offer>
        </template>

        <template v-else-if="offers.length == 1">
            <offer :title="The offer" :data="offers[0]"></offer>
        </template>
    </div>
</template>

Based on the number of offers, I choose how many to render.

Question: How do I efficiently get/count the number of <offer> components? I also need that number to be reactive.


回答1:


There's no clean way how.

You could count the children of the current instance that are of a specific type. But you would have to call the "recount" logic on update hook (as well as mounted).

Example:

Vue.component('offer', {
  name: 'Offer',
  template: '<span> offer </span>'
})
new Vue({
  el: '#app',
  data: {
    offers: [1, 2],
    offerCount: 0
  },
  methods: {
    updateOfferCount() {
      this.offerCount = this.$children.filter(child => child.constructor.options.name === 'Offer').length;
    }
  },
  updated() {
    this.updateOfferCount()
  },
  mounted() {
    this.updateOfferCount()
  }
})
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <div>
    <template v-if="offers.length > 3">
        <a href="#">View all offers here</a>
    </template>

    <template v-else-if="offers.length > 1">
        <offer v-for="offer in offers" :data="offer"></offer>
    </template>

    <template v-else-if="offers.length == 1">
        <offer :data="offers[0]"></offer>
     </template>
  </div>
  <br>
  <button @click="offers.push(123)">Add Offer</button> offerCount: {{ offerCount }}
</div>



回答2:


I'm answering this based solely on the idea that you want to count instantiations and destructions of Offer components. I'm not sure why you don't just count offers.length. Maybe other things can trigger instantiations.

Have the component emit events on creation and destruction and have the parent track accordingly.

Alternatively (and maybe overkill) you could use Vuex and create a store that the Offer commits to on creation and destruction. This means that you don't have to manually attach @offer-created/destroyed directives every time you put an <offer> in your markup.

Both methods are included in the following example:

const store = new Vuex.Store({
  strict: true,
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    },
    decrement(state) {
      state.count--;
    }
  }
});

const Offer = {
  props: ["data"],
  template: "<div>{{data.name}}</div>",
  created() {
    console.log("Created");
    this.$emit("offer-created");

    this.$store.commit("increment");
  },
  destroyed() {
    console.log("Destroyed");
    this.$emit("offer-destroyed");

    this.$store.commit("decrement");
  }
};

const app = new Vue({
  el: "#app",
  store,
  components: {
    offer: Offer
  },
  data() {
    return {
      offers: [],
      offerCount: 0
    };
  },
  computed: {
    offerCountFromStore() {
      return this.$store.state.count;
    }
  },
  methods: {
    offerCreated() {
      this.offerCount++;
    },
    offerDestroyed() {
      this.offerCount--;
    },
    addOffer() {
      this.offers.push({
        name: `Item: ${this.offers.length}`
      });
    },
    removeOffer() {
      this.offers.pop();
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.1/vuex.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.min.js"></script>
<div id="app">
  <div>Offer instances: {{offerCount}}</div>
  <div>Offer instances (from store): {{offerCountFromStore}}</div>
  <div>
    <div v-if="offers.length > 3">
      <a href="#">View all offers here</a>
    </div>
    <div v-else-if="offers.length > 1">
      <offer @offer-created="offerCreated" @offer-destroyed="offerDestroyed" v-for="offer in offers" :data="offer"></offer>
    </div>
    <div v-else-if="offers.length == 1">
      <offer @offer-created="offerCreated" @offer-destroyed="offerDestroyed" :data="offers[0]"></offer>
    </div>
  </div>
  <div>
    <button @click.prevent="addOffer">Add</button>
    <button @click.prevent="removeOffer">Remove</button>
  </div>
</div>

The problem with trying to use $children is that it is, inherently, not reactive:

The direct child components of the current instance. Note there’s no order guarantee for $children, and it is not reactive. If you find yourself trying to use $children for data binding, consider using an Array and v-for to generate child components, and use the Array as the source of truth.



来源:https://stackoverflow.com/questions/50047639/count-the-occurrences-of-a-child-component

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