How can I collect selected checkboxes from all chi

2019-06-06 00:30发布

I have a table where the row elements are all populated by child components. There is a checkbox in each of these child components. Now I want to get all checked checkboxes at once. I could use prefs emit as two way binding and update an array or object on the parent but I am wondering if there is better way for this.

Here a short example for the template part:

<table>
    <thead>
        <tr>
            <th> Check </th>
            <th> Title </th>
        </tr>
    </thead>
    <list-tbody v-for="element in elements" :element="element"> </list-tbody>
</table>

and this is the child component

<tbody>
    <tr>
        <td>
            <input type="checkbox">
        </td>
        <td> {{element.title}} </td>
    </tr>
</tbody>

2条回答
一纸荒年 Trace。
2楼-- · 2019-06-06 00:35

As mentioned in the comments you could handle this in two ways:

  • Use Vuex and mutate the elements array from the child component.
  • Emit an event on each selection click event to the parent and the parent will update the elements array.

You prefer the second because you're not using Vuex and that's OK.

Once you're having the data "linked" between child component and parent you can use a filter method to only show the selected elements.

Please have a look at the demo below or the fiddle from my comment.

const listTbodyVuex = {
props: ['element'],
	template: `
  	<tbody>
    <tr>
        <td>
            <input type="checkbox" @click="selected">
        </td>
        <td> {{element.title}} </td>
    </tr>
</tbody>
  `,
  methods: {
  	...Vuex.mapMutations(['changeSelection']),
  	selected(evt) {
    	//console.log('clicked', evt.target.checked, this.changeSelection)
      // changeSelection mutation could be also called with-out mapping
      // this.$store.commit('changeSelection', ...);
      this.changeSelection({
      	id: this.element.id, selected: evt.target.checked
      });
    }
  }
}

const listTbodyEvents = {
	props: ['element'],
	template: `
  	<tbody>
    <tr>
        <td>
            <input type="checkbox" @click="selected">
        </td>
        <td> {{element.title}} </td>
    </tr>
</tbody>
  `,
  methods: {
  	selected(evt) {
    	console.log('clicked', evt.target.checked)
      this.$emit('selected', {
      	element: this.element,
        newSelection: evt.target.checked
      })
    }
  }
}

const store = new Vuex.Store({
	state: {
  	elements: [
      {
      	id: 0,
      	title: 'first',
        selected: false
      },
      {	
      	id: 1,
      	title: 'second',
        selected: false
      },
      {
      	id: 2,
      	title: 'third',
        selected: false
      }
      ]
  },
  mutations: {
  	changeSelection(state, {id, selected}) {
    	let element = state.elements
      	.filter((element) => element.id === id)[0];
      element.selected = selected;
      //console.log('update element', JSON.parse(JSON.stringify(element)));
      Vue.set(state.elements, element.id, element);
    }
  }
})

new Vue({
	el: '#app',
  store,
  data() {
  	return {
    	elements: [
      {
      	id: 0,
      	title: 'first',
        selected: false
      },
      {	
      	id: 1,
      	title: 'second',
        selected: false
      },
      {
      	id: 2,
      	title: 'third',
        selected: false
      }
      ]
    }
  },
  computed: {
  	...Vuex.mapState({
    	vuexElements: (state) => state.elements
    })
  },
  components: {
  	listTbodyEvents,
    listTbodyVuex
  },
  methods: {
  	updateElement(data) {
    	let element = this.elements
      	.filter((element) => element.id === data.element.id)[0];
      element.selected = data.newSelection;
      // console.log('update', element)
    },
    filterSelected(data) {
    	// console.log('filter',  data.filter((item) => console.log(item.selected)))
    	return data.filter((item) => item.selected);
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.0.0/vuex.js"></script>
<div id="app">
<h1>Example with vuex</h1>
<table>
    <thead>
        <tr>
            <th> Check </th>
            <th> Title </th>
        </tr>
    </thead>
    <list-tbody-vuex v-for="element in elements" :element="element" :key="element.id"> </list-tbody-vuex>
</table>
  <pre>only selected: {{filterSelected(vuexElements)}}</pre>
  <pre>{{vuexElements}}</pre>
  
<hr/>
<h1>Example with events</h1>
<table>
    <thead>
        <tr>
            <th> Check </th>
            <th> Title </th>
        </tr>
    </thead>
    <list-tbody-events v-for="element in elements" :element="element" :key="element.id" @selected="updateElement"> </list-tbody-events>
</table>
  <pre>only selected: {{filterSelected(elements)}}</pre>
  <pre>{{elements}}</pre>
</div>

查看更多
做个烂人
3楼-- · 2019-06-06 00:44

You should really stick to emitting values to maintain separation of your components. That being said, you can do the following if you really wanted to grab the data all at once:

First, you'll need a data attribute in your child component that your checkbox uses with v-model. For the sake of this example, let's just call it checkbox_value. Once you've done that, you can do something like the following in your parent component method:

var checkbox_values = [];
this.$children.forEach(function(child) {
    //you can check the child type here if you have other non-checkbox children
    checkbox_values.push(child.$data.checkbox_value);
});

Of course, I wouldn't encourage you to do something like this, but you'll have to make that judgement call.

Note: The above will return values only. You could push object key/value pairs instead!

查看更多
登录 后发表回答