How can I add button selected if clicked on the bo

2020-04-18 04:28发布

问题:

I get reference from here :

https://bootstrap-vue.js.org/docs/components/card/#card-groups

https://bootstrap-vue.js.org/docs/components/button/#pressed-state-and-toggling

My vue component like this :

<template>
    ...
        <b-card-group deck v-for="row in formattedItems">
            <b-card :title="item.title"
                    img-src="http://placehold.it/140?text=No image"
                    img-alt="Img"
                    img-top
                    v-for="item in row">
                <p class="card-text">
                    {{item.price}}
                </p>
                <p class="card-text">
                    {{item.country}}
                </p>
                <div slot="footer">
                    <b-button-group size="sm">
                        <b-button :pressed.sync="oriPress" variant="outline-primary">Original</b-button>
                        <b-button :pressed.sync="kwPress" variant="outline-primary">Kw</b-button>
                    </b-button-group>
                    <b-btn variant="primary" block>Add</b-btn>
                </div>
            </b-card>
        </b-card-group>
    ....
</template>

<script>
    export default {
        ..
        data () {
            return{
                items: [
                     {id:1, title:'chelsea', price:900, country: 'england'},
                     {id:2, title:'liverpool', price:800, country: 'england'},
                     {id:3, title:'mu', price:700, country: 'england'},
                     {id:4, title:'city', price:600, country: 'england'},
                     {id:5, title:'arsenal', price:500, country: 'england'},
                     {id:6, title:'tottenham', price:400, country: 'england'},
                     {id:7, title:'juventus', price:300, country: 'italy'},
                     {id:8, title:'madrid', price:200, country: 'span'},
                     {id:9, title:'barcelona', price:100, country: 'span'},
                     {id:10, title:'psg', price:50, country: 'france'}
                ],
                oriPress: true,
                kwPress: false
            }
        },
        mounted: function () {
            this.getItems()
        },
        computed: {
            formattedItems() {
                return this.items.reduce((c, n, i) => {
                    if (i % 4 === 0) c.push([]);
                    c[c.length - 1].push(n);
                    return c;
                }, []);
            }
        }
    }
</script>

If the script executed, button original in all box active and button kw in all box inactive

That is what I expected. But my problem is when I click the kw button or the original button, all buttons are active or inacive. I want it active only on the button selected on each box

For example there are 10 box boxes. When I click the original button in the third box, the original button in the third box will be active and the kw button will be inactive. When I click the button kw in the ninth box, the button kw in the ninth box will be active and the button original will be inactive. So do the others

How can I do it?

回答1:

The problem is the same oriPress and kwPress data properties are used for all items. You could move those properties into items[] so that they're unique to each item:

// script
data() {
  return {
    items: [
      {id: 1, oriPress: true, kwPress: false, ...},
      {id: 2, oriPress: true, kwPress: false, ...},
    ]
  }
}

//template
<b-card-group v-for="row in formattedItems">
  <b-card v-for="item in row">

      <b-button :pressed.sync="item.oriPress">Original</b-button>
      <b-button :pressed.sync="item.kwPress">Kw</b-button>

  </b-card>
</b-card-group>

...but I assume the shape of items[] cannot be changed. The alternative is to create a map of oriPress and kwPress properties (one per item). This could be done with a watcher on items[] that initializes oriPress and kwPress each to a map of item IDs to Booleans:

// script
data() {
  return {
    items: [...],
    oriPress: {},
    kwPress: {},
  }
},
watch: {
  items: {
    immediate: true,
    handler(value) {
      this.oriPress = value.reduce((p,c) => {
        p[c.id] = true;
        return p;
      }, {});

      this.kwPress = value.reduce((p,c) => {
        p[c.id] = false;
        return p;
      }, {});
    }
  }
},


//template
<b-card-group v-for="row in formattedItems">
  <b-card v-for="item in row">

      <b-button :pressed.sync="oriPress[item.id]">Original</b-button>
      <b-button :pressed.sync="kwPress[item.id]">Kw</b-button>

  </b-card>
</b-card-group>

new Vue({
  el: '#app',
  data() {
    return{
      items: [
        {id:1, title:'chelsea', price:900, country: 'england'},
        {id:2, title:'liverpool', price:800, country: 'england'},
        {id:3, title:'mu', price:700, country: 'england'},
        {id:4, title:'city', price:600, country: 'england'},
        {id:5, title:'arsenal', price:500, country: 'england'},
        {id:6, title:'tottenham', price:400, country: 'england'},
        {id:7, title:'juventus', price:300, country: 'italy'},
        {id:8, title:'madrid', price:200, country: 'span'},
        {id:9, title:'barcelona', price:100, country: 'span'},
        {id:10, title:'psg', price:50, country: 'france'}
      ],
      oriPress: {},
      kwPress: {}
    }
  },
  watch: {
    items: {
      immediate: true,
      handler(value) {
        this.oriPress = value.reduce((p,c) => {
          p[c.id] = true;
          return p;
        }, {});
        this.kwPress = value.reduce((p,c) => {
          p[c.id] = false;
          return p;
        }, {});
      }
    }
  },
  computed: {
    formattedItems() {
      return this.items.reduce((c, n, i) => {
        if (i % 4 === 0) c.push([]);
        c[c.length - 1].push(n);
        return c;
      }, []);
    }
  }
})
<script src="https://unpkg.com/vue@2.5.17"></script>

<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css"/>
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.css"/>
<script src="//unpkg.com/babel-polyfill@latest/dist/polyfill.min.js"></script>
<script src="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.js"></script>

<div id="app">
  <b-card-group deck v-for="row in formattedItems">
    <b-card :title="item.title"
            img-src="http://placehold.it/140?text=No image"
            img-alt="Img"
            img-top
            v-for="item in row">
      <p class="card-text">
        {{item.price}}
      </p>
      <p class="card-text">
        {{item.country}}
      </p>
      <div slot="footer">
        <b-button-group size="sm">
          <b-button :pressed.sync="oriPress[item.id]" variant="outline-primary">Original</b-button>
          <b-button :pressed.sync="kwPress[item.id]" variant="outline-primary">Kw</b-button>
        </b-button-group>
        <b-btn variant="primary" block>Add</b-btn>
      </div>
    </b-card>
  </b-card-group>
</div>