Closed keevitaja closed 7 years ago
Use render-header
Here's a little render-header snippet if anyone else needs this feature. It is bit tricky to use element ui components in the header.
if (this.dataFilters[k].type == 'date-picker-range') {
result = h('el-date-picker', {
props: {
size: 'small',
type: 'daterange'
},
style: 'font-weight: normal',
on: {
input(e) {
if (! e) return
// bind selected range to datapicker element
result.componentInstance.$data.currentValue = e
// v-model
self.dataFilters[k].value = e
}
}
})
} else {
result = h('el-input', {
props: {
size: 'small'
},
style: 'font-weight: normal',
on: {
input(e) {
// v-model
self.dataFilters[k].value = e
}
}
})
}
return h('div', { style: 'margin-bottom: 10px' }, [
h('div', null, e.column.label), result
])
Or perhaps there is a better way?
You can use JSX
Hi , I use your example with render-headers , but I don't now how to filter my data table (called usersList) on "firstname" input event.
My table column :
<el-table-column sortable render-header="filterFirstname" prop="firstname" label="Prénom" min-width="100"> </el-table-column>
My method:
filterFirstname(h,{column,$index})
{
let self = this
let result = h('el-input', {
props: {
size: 'small'
},
style: 'font-weight: normal',
on: {
input(query) {
//TODO : filter rows by query
}
}
})
return h('div', { style: 'margin-bottom: 10px' }, [
h('div', null, column.label), result
])
},
Can you help me ?
@keevitaja can you paste your full code??
Sorry guys... It has been a while i worked on this project, but the full component code is here:
<template>
<div class="ajax-table">
<div class="row" v-if="filterable || actions">
<div class="col-md-3">
<div class="form-group" v-if="filterable">
<el-input v-model="filter"></el-input>
</div>
</div>
<div class="col-md-3 col-md-offset-6">
<div class="form-group">
<el-select v-model="actionsUrl" :placeholder="actionsChoose" v-if="actions">
<el-option
v-for="item in actions"
:label="item.name"
:value="item.url"
:key="item.id"
></el-option>
</el-select>
<form style="display:none;" method="POST" ref="actionsForm">
<input type="hidden" name="_token" :value="token">
<template v-for="row in rows">
<input type="hidden" name="items[]" :value="row.id" v-if="row.checked">
</template>
</form>
</div>
</div>
</div>
<el-table :data="rows" stripe @selection-change="doSelection">
<el-table-column type="selection" width="50" v-if="actions"></el-table-column>
<template v-for="column, k, i in columns">
<el-table-column :label="column" :width="(k == 'id') ? '100' : 'auto'" :render-header="renderHeader">
<template scope="scope"><span v-html="objectPath(rows[scope.$index], k)"></span></template>
</el-table-column>
</template>
<el-table-column v-if="buttons.length > 0" label="" align="right">
<template scope="scope">
<template v-for="button in rows[scope.$index].buttons">
<confirmed-destroy classes="el-button el-button--danger el-button--small" :url="button.url" v-if="button.type == 'destroy'">
{{ button.label }}
</confirmed-destroy>
<form
style="display: inline;"
method="POST"
:action="button.url"
v-else-if="button.type == 'store' || button.type == 'update'"
>
<input type="hidden" name="_token" :value="token">
<input type="hidden" name="_method" value="PATCH" v-if="button.type == 'update'">
<template v-for="value, key in button.payload">
<input type="hidden" :name="key" :value="value">
</template>
<a class="el-button el-button--default el-button--small" :href="button.url" @click.prevent="submit">
{{ button.label }}
</a>
</form>
<a class="el-button el-button--default el-button--small" :href="button.url" v-else>{{ button.label }}</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="paginate-table" v-if="pagination.total > dataParams.perpage">
<el-pagination
layout="sizes, prev, pager, next"
:total="pagination.total"
:page-size="dataParams.perpage"
:current-page="pagination.current"
:page-sizes="[10, 20, 30, 40, 100]"
@current-change="paginate"
@size-change="updatePageSize"
></el-pagination>
</div>
</div>
</template>
<script>
import regex from './../../utils/regex.js'
import objectPath from 'object-path'
export default {
props: {
actions: {
default: false
},
url: {
type: String,
required: true
},
columns: {
type: Object,
required: true
},
filterable: {
type: Boolean,
default: false
},
filters: {
type: Object,
default: ()=> {
return {}
}
},
anchors: {
type: Object,
default: ()=> {
return {}
}
},
buttons: {
type: Array,
default: ()=> {
return []
}
},
noresults: {
type: String,
default: objectPath.get(window.vars, 'translations.table.noresults', 'script.table.noresults')
},
params: {
type: Object,
default: ()=> {
return {
paginate: 10,
filters: {},
perpage: 10
}
}
},
payload: {
type: Object,
default: ()=> {
return {}
}
}
},
data() {
return {
actionsChoose: objectPath.get(window.vars, 'script.table.actions', 'script.table.actions'),
actionsUrl: false,
dataFilters: {},
filter: '',
id: 'ajax-table-' + this._uid,
loaded: false,
rows: [],
toggleActions: false,
token: window.vars.csrf,
pagination: {
current: 0,
total: 0
}
}
},
computed: {
dataParams() {
let params = this.params
params.filters = this.dataFilters
return params
}
},
methods: {
applyAnchors(rows) {
for (let column in this.anchors) {
let url = this.anchors[column]
let fields = regex(url, /-([a-z_.]+)-/gi)
for (let row of rows) {
let parsed
for (let field of fields) {
parsed = url.replace('-' + field + '-', objectPath.get(row, field))
}
objectPath.set(row, column, `<a href="${parsed}">${objectPath.get(row, column)}</a>`)
}
}
},
applyButtons(rows) {
for (let row of rows) {
row.buttons = []
for (let button of this.buttons) {
let fields = regex(button.url, /-([a-z_.]+)-/gi)
let parsed
for (let field of fields) {
parsed = button.url.replace('-' + field + '-', row[field])
}
row.buttons.push({
url: parsed,
label: button.label,
type: button.type,
payload: button.payload
})
}
}
},
camel(str) {
return str.replace(/^([A-Z])|[\s-_](\w)/g, function(match, p1, p2, offset) {
if (p2) return p2.toUpperCase()
return p1.toLowerCase()
})
},
load(page) {
this.dataParams.page = page
let options = {
params: this.dataParams
}
this.$http(this.url, options).then((res)=> {
this.rows = res.data.data.map((o)=> {
o.checked = false
return o
})
if (this.rows.length > 0) this.loaded = true
this.pagination.current = res.data.current_page
this.pagination.total = res.data.total
this.applyAnchors(this.rows)
this.applyButtons(this.rows)
}).catch((err)=> console.log(err))
},
doSelection(rows) {
for (let row of this.rows) {
row.checked = false
}
for (let row of rows) {
row.checked = ! row.checked
}
},
paginate(e) {
this.load(e)
},
updatePageSize(e) {
this.dataParams.perpage = e
this.pagination.current = 1
this.load(1)
},
renderHeader(h, e) {
const i = this.actions ? e.$index - 1 : e.$index
const k = Object.keys(this.columns)[i]
const self = this
let result
if ( ! this.dataFilters[k]) {
return e.column.label
}
if (this.dataFilters[k].type == 'date-picker-range') {
result = h('el-date-picker', {
props: {
size: 'small',
type: 'daterange'
},
style: 'font-weight: normal',
on: {
input(e) {
if (! e) return
result.componentInstance.$data.currentValue = e
self.dataFilters[k].value = e
}
}
})
} else {
result = h('el-input', {
props: {
size: 'small'
},
style: 'font-weight: normal',
on: {
input(e) {
self.dataFilters[k].value = e
}
}
})
}
return h('div', { style: 'margin-bottom: 10px' }, [
h('div', null, e.column.label), result
])
},
seek(page) {
if (page > 0 && page <= this.paginator.total) {
return this.load(page)
}
},
singleFilterAdd(column) {
this.dataParams.filters[column] = this.$refs['filters_' + column][0].value
this.load(1)
},
dateFilterAdd(column, type) {
console.log('test')
},
objectPath(object, key, def = null) {
return objectPath.get(object, key, def)
},
submit(evt) {
evt.target.parentElement.submit()
}
},
watch: {
actionsUrl() {
if (this.actionsUrl) {
let form = this.$refs.actionsForm
form.action = this.actionsUrl
form.submit()
}
},
filter() {
this.dataParams.filter = this.filter
this.load(1)
},
toggleActions() {
this.rows.map((o)=> {
o.checked = this.toggleActions
})
}
},
mounted() {
this.load(1)
for (let column in this.filters) {
let type = this.filters[column]
this.$set(this.dataFilters, column, {})
this.$set(this.dataFilters[column], 'value', '')
this.dataFilters[column].type = type
}
this.$watch('dataFilters', ()=> {
this.load(1)
}, { deep: true })
}
}
</script>
This is demo for table header add el-popover(use vue-data-tables plugin), web address:https://github.com/cag2050/vue_tables_xlsx_demo/blob/master/src/views/useDataTablesComp.vue
Just as an example, for reference. To render the following column header:
<div class="phase-header">
<div class="phase-header__title">Phase 1</div>
<span class="phase-header__details">some details</span>
</div>
You need the following code:
<template>
<el-table-column
label="Phase 1"
:render-header="renderPhaseHeader"
/>
</template>
<script>
...
methods: {
renderPhaseHeader (h, { column }) {
return h('div', { 'class': 'phase-header' }, [
h('div', { 'class': 'phase-header__title' }, column.label),
h('span', { 'class': 'phase-header__details' }, 'some details')
])
}
}
</script>
I think this might be cleaner
first, make athother component base on el-table-column
vue.component('my-table-column', {
props: {
columnKey: String,
// others
},
methods: {
renderHeader(h, {column, store}){
this.column = column
this.store = store
let header = h('span', [column.label, h('span', {class: 'el-table__column-filter-trigger'}, [this.$scopedSlots.filtering(this.updateChange)]) ])
return header
},
updateChange(newVal){
this.store.commit('filterChange', { column: this.column, values: newVal})
this.store.updateAllSelected()
this.column.filteredValue = newVal
}
},
render(h){
return (
<el-table-column
column-key={ this.columnKey }
scopedSlots={ this.$scopedSlots }
render-header={ this.$scopedSlots.filterSlot ?this.renderHeader : null}>
</el-table-column>
)
}
})
then, using it just change the tag, add the slot, and binding the value change event.
<my-table-column column-key="key">
<template slot-scope="scope">
<span>{{ scope.row }}</span>
</template>
<template slot="filterSlot" slot-scope="whatever">
<el-input @input="whatever"></el-input>
</template>
</my-table-column>
How to render header with an el-select with some options. E.g. a select list like below.
<el-select v-model="status_filter" size="mini" clearable @change="getList" placeholder="Select">
@jagannathprasad Here's an example of me using el-select in an header. Hope it helps.
<el-table-column prop="type" label="Type"
:render-header="renderSelectHeader"
>
data() {
return {
dialog: {
visible: false,
loading: false,
},
expense: {
date: null,
items: [],
new_item: { date: null, type: null }
},
expenses: [],
expenses_ref: null,
expense_types: [{
value: 'food', name: 'Food',
}]
};
},
renderSelectHeader(createElement, { column, store }) {
let element = createElement('div', { class: 'el-table-header-resource' }, [
createElement('el-select', {
props: { placeholder: column.label, value: this.expense.new_item[column.property]},
on: {
input: (value) => {
this.expense.new_item[column.property] = value;
}
}
}, this.expense_types.map((item) => {
return createElement('el-option', {props: { key: item.value, value: item.value, label: item.name}});
}))
]);
element.fnScopeId = this.$options._scopeId;
element.fnContext = this;
return element;
},
hi guys,i want to do the same with render-header,but there's a little difference that i use reusable components as tag. now,here comes problem ,i want to callback function in the reusable components with ref,but it doesn't work. the codes as follow:
` handleRenderHeader (h,{column,$index}) {
let self = this;
let item = column.label;
const source = this.filterSettingMap.get(column.property);
const data = {
on: {
input(val) {
self.filters[column.property] = val;
},
},
props: {
source: source,
value: self.filters[column.property],
},
ref: 'filterValue_'+source.id,
}
return (
<span>{item}
<FilterValue {...data}></FilterValue>
</span>
)
},`
`watch:{ filters: { handler(form) { console.log(this.$refs); window.setTimeout(() => { const obj = {} for (let key in form) { const map = this.filterSettingMap.get(key) const value = form[key]
if (value === '' || value.length === 0 || (value.length === 2 && value[0] === '' && value[1] === '')) {
obj[key] = false
}else {
const name = map['name']
const str = 'filterValue_' + key;
// this.$refs have no filterValue_... instance
const label = this.$refs[str][0].getLabel();
obj[key] = { name, key, label, value }
}
}
this.fillListFilter(obj)
}, 0)
},
deep: true,
}
},`
I'm using this as well. I can filter per column, however, i also support sorting per column. The probblem is that when i click the textfield inside the header, the sorting is triggered also.
Tried to prevent bubbling inside the click event of the textfield but without luck. Any ideas on this?
renderHeader: function (h, {column, $index}) { var self = this; return h('span', {class: 'header'}, [ h('span', {class: 'header-label'}, column.label), h('el-input', { props: { size: 'mini' }, style: 'font-weight: debouncedebnormal; padding: 0', on: { input: function (e) { e.preventPropagation(); } } }) ]) },
@kife-design you can try to bind the pevent event in nativeOn object ,just like
...`
nativeOn: {
click(e){
e.stopPropagation();
}
}
`
Existing Component
Yes
Component Name
el-table-column
Description
Hello!
Could i have a custom template for the column header?
I need to have some filtering using el-input.
Endresult should be similar to