Skip to content

Commit 906a91d

Browse files
VdustRmarcosmoura
authored andcommitted
feat(MdTable): reactive selection (#1358)
* fix(MdTable): multiple selection with table sorting an easy way to handle multiple selection fix #1348 * refactor(MdTable): single selection using instance comparing instead of id comparing both `MdTable` and `MdTableRow` emit `md-selected` event to avoid breaking change * feat(MdTable): new props `:md-selected-value.sync` for reactive selection fix #1292 * fix(MdTable): `syncSelectedValue` without `mdSelectedValue` in multiple selecting mode * fix(MdTableHeadSelection): fix `allSelected`, now disabled row could be tick from `md-selected-value * fix(MdTableRow): remove `md-selected` event too many duplicated events on a selection. It should be emit from `MdTable` BREAKING CHANGE: no more `md-selected` event from `MdTableRow` * fix(MdTable): select event should be triggered after select update event * fix(MdTableHeadSelection): select all only take effect on selectable items
1 parent ba876d7 commit 906a91d

File tree

5 files changed

+97
-59
lines changed

5 files changed

+97
-59
lines changed

docs/app/pages/Components/Table/examples/TableSingle.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<template>
22
<div>
3-
<md-table v-model="people" md-card>
3+
<md-table v-model="people" md-card @md-selected="onSelect">
44
<md-table-toolbar>
55
<h1 class="md-title">Selection Colors</h1>
66
</md-table-toolbar>
77

8-
<md-table-row slot="md-table-row" slot-scope="{ item }" :class="getClass(item)" md-selectable="single" @md-selected="onSelect">
8+
<md-table-row slot="md-table-row" slot-scope="{ item }" :class="getClass(item)" md-selectable="single">
99
<md-table-cell md-label="ID" md-sort-by="id" md-numeric>{{ item.id }}</md-table-cell>
1010
<md-table-cell md-label="Name" md-sort-by="name">{{ item.name }}</md-table-cell>
1111
<md-table-cell md-label="Email" md-sort-by="email">{{ item.email }}</md-table-cell>

src/components/MdTable/MdTable.vue

+43-15
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
v-for="(item, index) in value"
2828
:key="getRowId(item[mdModelId])"
2929
:md-id="getRowId(item[mdModelId])"
30-
:md-index="index">
30+
:md-index="index"
31+
:md-item="item">
3132
<slot name="md-table-row" :item="item" />
3233
</md-table-row-ghost>
3334
</tbody>
@@ -69,7 +70,7 @@
6970
7071
return value
7172
}
72-
73+
7374
export default {
7475
name: 'MdTable',
7576
components: {
@@ -119,6 +120,9 @@
119120
return aAttr.localeCompare(bAttr)
120121
})
121122
}
123+
},
124+
mdSelectedValue: {
125+
type: [Array, Object]
122126
}
123127
},
124128
data () {
@@ -130,8 +134,8 @@
130134
sort: null,
131135
sortOrder: null,
132136
singleSelection: null,
133-
selectedItems: {},
134-
selectable: {},
137+
selectedItems: [],
138+
selectable: [],
135139
fixedHeader: null,
136140
contentPadding: null,
137141
contentEl: null,
@@ -142,7 +146,8 @@
142146
sortTable: this.sortTable,
143147
manageItemSelection: this.manageItemSelection,
144148
getModel: this.getModel,
145-
getModelItem: this.getModelItem
149+
getModelItem: this.getModelItem,
150+
selectingMode: null
146151
}
147152
}
148153
},
@@ -158,7 +163,7 @@
158163
return Object.keys(this.MdTable.items).length
159164
},
160165
selectedCount () {
161-
return Object.keys(this.MdTable.selectedItems).length
166+
return this.MdTable.selectedItems.length
162167
},
163168
headerStyles () {
164169
if (this.mdFixedHeader) {
@@ -213,6 +218,15 @@
213218
handler () {
214219
this.MdTable.hasValue = this.hasValue
215220
}
221+
},
222+
'MdTable.selectedItems' (val) {
223+
this.select(val)
224+
},
225+
'MdTable.singleSelection' (val) {
226+
this.select(val)
227+
},
228+
mdSelectedValue () {
229+
this.syncSelectedValue()
216230
}
217231
},
218232
methods: {
@@ -251,24 +265,38 @@
251265
getModelItem (index) {
252266
return this.value[index]
253267
},
254-
manageItemSelection (index) {
255-
if (this.MdTable.selectedItems[index]) {
256-
this.$delete(this.MdTable.selectedItems, index)
268+
manageItemSelection (item) {
269+
if (this.MdTable.selectedItems.includes(item)) {
270+
this.MdTable.selectedItems = this.MdTable.selectedItems.filter(target => target !== item)
257271
} else {
258-
this.$set(this.MdTable.selectedItems, index, this.value[index])
272+
this.MdTable.selectedItems.push(item)
259273
}
260-
261-
this.sendSelectionEvent()
262-
},
263-
sendSelectionEvent () {
264-
this.$emit('md-selected', Object.values(this.MdTable.selectedItems))
265274
},
266275
sortTable () {
267276
if (Array.isArray(this.value)) {
268277
this.$emit('input', this.mdSortFn(this.value))
269278
}
279+
},
280+
select (val) {
281+
this.$emit('update:mdSelectedValue', val)
282+
this.$emit('md-selected', val)
283+
},
284+
syncSelectedValue () {
285+
switch (this.MdTable.selectingMode) {
286+
case 'single':
287+
this.MdTable.singleSelection = this.mdSelectedValue
288+
break
289+
case 'multiple':
290+
this.MdTable.selectedItems = this.mdSelectedValue || []
291+
break
292+
}
270293
}
271294
},
295+
async created () {
296+
// wait for `selectingMode` from `TableRow`
297+
await this.$nextTick()
298+
this.syncSelectedValue()
299+
},
272300
mounted () {
273301
this.setContentEl()
274302

src/components/MdTable/MdTableHeadSelection.vue

+13-22
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<md-table-head class="md-table-cell-selection" v-if="selectableCount">
33
<div class="md-table-cell-container">
4-
<md-checkbox v-model="allSelected" :disabled="isDisabled" @change="onChange" />
4+
<md-checkbox :model="allSelected" :disabled="isDisabled" @change="onChange" />
55
</div>
66
</md-table-head>
77
</template>
@@ -15,9 +15,6 @@
1515
MdTableHead
1616
},
1717
inject: ['MdTable'],
18-
data: () => ({
19-
allSelected: false
20-
}),
2118
computed: {
2219
selectableCount () {
2320
return Object.keys(this.selectable).length
@@ -30,28 +27,22 @@
3027
},
3128
selectedItems () {
3229
return this.MdTable.selectedItems
33-
}
34-
},
35-
watch: {
36-
selectedItems: {
37-
immediate: true,
38-
deep: true,
39-
handler (items) {
40-
window.setTimeout(() => {
41-
const countSelected = Object.keys(items).length
42-
43-
if (this.selectableCount > 0 && countSelected > 0) {
44-
this.allSelected = countSelected === this.selectableCount
45-
}
46-
}, 10)
30+
},
31+
allSelected () {
32+
if (this.selectableCount === 0) {
33+
return false
4734
}
35+
36+
return this.selectable.every(item => this.selectedItems.includes(item))
4837
}
4938
},
5039
methods: {
51-
onChange () {
52-
Object.values(this.MdTable.selectable).forEach(callback => {
53-
callback(this.allSelected)
54-
})
40+
onChange (val) {
41+
if (val) {
42+
this.MdTable.selectedItems = this.selectedItems.concat(this.selectable.filter(item => !this.selectedItems.includes(item)))
43+
} else {
44+
this.MdTable.selectedItems = this.selectedItems.filter(item => !this.selectable.includes(item))
45+
}
5546
}
5647
}
5748
}

src/components/MdTable/MdTableRow.vue

+36-19
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
...MdPropValidator('md-selectable', ['multiple', 'single'])
2828
},
2929
mdDisabled: Boolean,
30-
mdAutoSelect: Boolean
30+
mdAutoSelect: Boolean,
31+
mdItem: Object
3132
},
3233
inject: ['MdTable'],
3334
data: () => ({
@@ -39,7 +40,7 @@
3940
return Object.keys(this.MdTable.selectable).length
4041
},
4142
isSingleSelected () {
42-
return this.MdTable.singleSelection === this.mdId
43+
return this.MdTable.singleSelection === this.mdItem
4344
},
4445
hasMultipleSelection () {
4546
return this.MdTable.hasValue && this.mdSelectable === 'multiple'
@@ -55,6 +56,9 @@
5556
'md-selected-single': this.isSingleSelected
5657
}
5758
}
59+
},
60+
isInSelectedItems () {
61+
return this.MdTable.selectedItems.includes(this.mdItem)
5862
}
5963
},
6064
watch: {
@@ -65,12 +69,20 @@
6569
this.addSelectableItem()
6670
}
6771
},
68-
mdId (newId, oldId) {
69-
this.removeSelectableItem(oldId)
70-
this.addSelectableItem(newId)
72+
isSelected (val) {
73+
let noChange = (val && this.isInSelectedItems) || (!val && !this.isInSelectedItems)
74+
75+
if (noChange) {
76+
return false
77+
}
78+
79+
this.MdTable.manageItemSelection(this.mdItem)
80+
},
81+
isInSelectedItems (val) {
82+
this.isSelected = val
7183
},
72-
isSelected () {
73-
this.MdTable.manageItemSelection(this.mdIndex)
84+
mdSelectable () {
85+
this.MdTable.selectingMode = this.mdSelectable
7486
}
7587
},
7688
methods: {
@@ -87,34 +99,39 @@
8799
this.isSelected = !this.isSelected
88100
},
89101
selectRowIfSingle () {
90-
if (this.MdTable.singleSelection === this.mdId) {
102+
if (this.MdTable.singleSelection === this.mdItem) {
91103
this.MdTable.singleSelection = null
92-
this.$emit('md-selected', null)
93104
} else {
94-
this.MdTable.singleSelection = this.mdId
95-
this.$emit('md-selected', this.MdTable.getModelItem(this.mdIndex))
105+
this.MdTable.singleSelection = this.mdItem
96106
}
97107
},
98108
selectRowIfMultiple () {
99109
if (this.mdAutoSelect) {
100110
this.toggleSelection()
101111
}
102112
},
103-
addSelectableItem (id) {
104-
if (this.hasMultipleSelection && !this.mdDisabled) {
105-
this.$set(this.MdTable.selectable, id || this.mdId, isSelected => {
106-
this.isSelected = isSelected
107-
})
113+
addSelectableItem () {
114+
if (!this.hasMultipleSelection || this.mdDisabled) {
115+
return
108116
}
117+
118+
if (this.MdTable.selectable.includes(this.mdItem)) {
119+
return
120+
}
121+
122+
this.MdTable.selectable.push(this.mdItem)
109123
},
110-
removeSelectableItem (id) {
111-
if (this.hasMultipleSelection) {
112-
this.$delete(this.MdTable.selectable, id || this.mdId)
124+
removeSelectableItem () {
125+
if (!this.hasMultipleSelection) {
126+
return
113127
}
128+
129+
this.MdTable.selectable = this.MdTable.selectable.filter(item => item !== this.mdItem)
114130
}
115131
},
116132
created () {
117133
this.addSelectableItem()
134+
this.MdTable.selectingMode = this.mdSelectable
118135
},
119136
beforeDestroy () {
120137
this.removeSelectableItem()

src/components/MdTable/MdTableRowGhost.vue

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
abstract: true,
55
props: {
66
mdIndex: [String, Number],
7-
mdId: [String, Number]
7+
mdId: [String, Number],
8+
mdItem: Object
89
},
910
render () {
1011
this.$slots.default[0].componentOptions.propsData.mdIndex = this.mdIndex
1112
this.$slots.default[0].componentOptions.propsData.mdId = this.mdId
13+
this.$slots.default[0].componentOptions.propsData.mdItem = this.mdItem
1214
1315
return this.$slots.default[0]
1416
}

0 commit comments

Comments
 (0)