Replacing content of default header of v-data-table - vuejs2

I have a Vue application that is using Vuetify 2.x v-data-table component. Inside the v-data-table component is a template for the defined slot :header. Within the template is another custom defined slot.
<v-data-table>
<template v-slot:header="props">
<tr>
<slot name="hdrCheckbox">
<th><v-checkbox :input-value="props.all" color="#c79121" :indeterminate="props.indeterminate" primary hide-details #click.stop="toggleAllSelected"></v-checkbox></th>
</slot>
<th v-for="header in props.headers" :key="header.text"
:class="['column sortable', dataTable.options.sortDesc ? 'desc' : 'asc', header.value === dataTable.options.sortBy ? 'active' : '']"
#click="$emit('change-sort', header.value)">
<v-icon small>arrow_upward</v-icon>
{{ header.text }}
</th>
</tr>
</template>
</v-data-table>
<data-table>
<template #hdrCheckbox="props"><th class="empty"></th></template>
</data-table>
This is the markup that the component renders:
<tr>
<th class="empty"></th>
</tr>
<thead class="v-data-table-header">
<tr>
<th role="columnheader" scope="col" aria-label="Topic" class="text-start">
<span>Topic</span>
</th>
<th role="columnheader" scope="col" aria-label="Current Interval" class="text-start">
<span>Current Interval</span>
</th>
<th role="columnheader" scope="col" aria-label="Interval Benchmark: Sorted ascending. Activate to sort descending." aria-sort="ascending" class="text-start sortable active asc">
<span>Interval Benchmark</span>
<i aria-hidden="true" class="v-icon notranslate v-data-table-header__icon mdi mdi-arrow-up theme--light" style="font-size: 18px;"></i>
</th>
<th role="columnheader" scope="col" aria-label="Add Date: Not sorted. Activate to sort ascending." aria-sort="none" class="text-start sortable">
<span>Add Date</span>
<i aria-hidden="true" class="v-icon notranslate v-data-table-header__icon mdi mdi-arrow-up theme--light" style="font-size: 18px;"></i>
</th>
</tr>
</thead>
Why does the content of the custom defined slot hdrCheckbox render outside of the thead tag?

Well that's how v-data-table works
It may feel strange first but overriding header slot of v-data-table does not replace it's default header content. You need to use hide-default-header prop to do that. And content of header slot is placed before default header content...
Also check the documentation for header slot - parameter passed into it is object { props: {...}, on: {...}} so using it like <template v-slot:header="props"> wont work (props.headers does not exist, props.props.headers does). Use <template v-slot:header="{ props }"> instead....

Related

Vue Data Table Headers All Appearing in One Column

I have a v-data-table and I am trying to print my headings, but with my code they are all appearing grouped into one column instead of across the entire table.
<template v-slot:header>
<thead>
<tr>
<div v-for="(itm, i) in hdrs" :key="i">
<th>
{{itm.value}}
</th>
</div>
</tr>
</thead>
</template>
Can anyone offer a suggestion as to how to resolve this issue please?
the loop should be done in the th elements and remove the div one :
<tr>
<tempalte v-for="(itm, i) in hdrs">
<th v-if="someCondition">
{{itm.value}}
</th>
</template>
</tr>

vuetifyjs v-simple-table - change the loading status of the button on click

I have a vuetifyjs v-simple-table. I am doing v-for to populate some data. I have a button in every row. When I click on the button, it should show the loading status (the clicked button). I have the following code
<v-simple-table>
<template v-slot:default>
<thead>
<tr>
<th>
Status
</th>
<th class="text-left">
Actions
</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in parcels" v-bind:key="index" :class="item.order.bgColorClass">
<td>
<div class="status">
<span class="a-blue">Notified </span>
<v-icon>mdi-star</v-icon>
</div>
</td>
<td>
<v-btn :ref="item.order.orderId" :loading="false" text class="table-btn" #click="AddItem(item.order.orderId)">
<v-img src="images/plus-i.svg"></v-img>
</v-btn>
</td>
</tr>
</tbody>
</template>
</v-simple-table>
The method is
methods:{
AddItem(orderId) {
this.$refs[orderId][0].loading = true;
},
The above method shows the loader but this is not the correct way I guess. I am getting the following error on console
[Vue warn]: Avoid mutating a prop directly since the value will be
overwritten whenever the parent component re-renders. Instead, use a
data or computed property based on the prop's value. Prop being
mutated: "loading"
How can I change the loading status of the button when I click on it in a proper way?
I would take that button and wrap it, e.g:
{
name: "wrapped-v-btn",
data(){
return {
loading: false
}
}
}
<template>
<v-btn
:loading="loading"
#click="loading=!loading"
v-bind="$attrs"
>
<v-img src="images/plus-i.svg"></v-img>
</v-btn>
</template>
...
<td>
<wrapped-v-btn text class="table-btn" />
</td>

How to add toggleSelectAll functionality checkbox to new header which is generated by header slot

I tried to customize vuetify data table header with slot.
I disabled default header and made a new header with v-slot:header.
The problem is that if I make new header, there is no checkbox which support toggleSelectAll functionality.
The code is below.
<template
v-slot:header="{ props: { headers } }"
>
<thead>
<tr>
<th v-for="header in headers" :key="header.value">
<v-simple-checkbox
color="purple"
v-if="header.value === 'data-table-select'"
></v-simple-checkbox>
<span v-else>{{header.text}}</span>
</th>
</tr>
</thead>
</template>
And here is the Codepen link.
https://codepen.io/em0809/pen/gOrJGjJ
I want to add toggleSelectAll functionality to above simple checkbox.
I tried with v-slot:header.data-table-select, but it's not working.
So, Please help me with this if you have experience.
Thank you.
You can achieve it in 1 step:
Add #input="on['toggle-select-all']" to your v-simple-checkbox.
<template #header="{ props, on }">
<thead>
<tr>
<th v-for="header in props.headers" :key="header.value">
<v-simple-checkbox
v-if="header.value === 'data-table-select'"
v-model="props.everyItem"
:indeterminate="props.someItems && !props.everyItem"
color="purple"
#input="on['toggle-select-all']"
></v-simple-checkbox>
<span v-else>{{ header.text }}</span>
</th>
</tr>
</thead>
</template>
#header overwrite #header.data-table-select when both of this is using.
You can achieve it in 4 steps:
Add ref to your v-data-table component
Pass additional v-data-table props to header slot
Set up your v-model (and indeterminate prop if needed)
Create "selectAll" method that should call toggleSelectAll by ref
Example code is shown below:
<v-data-table
ref="table"
:value="selected"
:items-per-page="itemsPerPage"
:headers="headers"
:items="desserts"
item-key="name"
show-select
class="elevation-1"
hide-default-header
>
<template v-slot:header="{ props: { headers, ...props } }">
<thead>
<tr>
<th v-for="header in headers" :key="header.value">
<v-simple-checkbox
v-model="props.everyItem"
:indeterminate="props.someItems && !props.everyItem"
color="purple"
v-if="header.value === 'data-table-select'"
#input="selectAll"
></v-simple-checkbox>
<span v-else>{{header.text}}</span>
</th>
</tr>
</thead>
</template>
</v-data-table>
...
methods: {
selectAll(val) {
this.$refs.table.toggleSelectAll(val)
}
}
...

How can I call ajax every loop in the vue js?

My vue component like this :
<template>
...
<v-card
max-width="1200"
class="mx-auto"
>
<v-container
class="pa-2"
fluid
>
<v-row>
<v-col
v-for="(item, i) in dataDoctor"
:key="i"
>
<v-card
>
<v-list-item three-line>
<v-list-item-avatar
size="125"
tile
>
<v-img :src="'https://via.placeholder.com/100x100'"></v-img>
</v-list-item-avatar>
<v-list-item-content class="align-self-start" :style="{'text-align':'left'}">
<v-list-item-title
class="headline mb-2"
v-text="item.docterName"
></v-list-item-title>
<v-list-item-subtitle v-text="item.specialistName"></v-list-item-subtitle>
<v-list-item-subtitle v-text="item.hospitaName"></v-list-item-subtitle>
</v-list-item-content>
</v-list-item>
<v-app-bar dark color="grey">
<v-toolbar-title>Weekly Schedule : {{item.hospitalName}}</v-toolbar-title>
<div class="flex-grow-1"></div>
<v-dialog
ref="dialog"
v-model="modal"
:return-value.sync="date"
persistent
width="390px"
>
<template v-slot:activator="{ on }">
<v-btn color="success" dark v-on="on">Call datepicker</v-btn>
</template>
<v-date-picker v-model="date" scrollable>
<div class="flex-grow-1"></div>
<v-btn text color="primary" #click="modal = false">Cancel</v-btn>
<v-btn text color="primary" #click="$refs.dialog.save(date)">OK</v-btn>
</v-date-picker>
</v-dialog>
</v-app-bar>
<v-simple-table>
<template v-slot:default>
<thead>
<tr>
<th class="text-left">Sun</th>
<th class="text-left">Mon </th>
<th class="text-left">Tue</th>
<th class="text-left">Wed </th>
<th class="text-left">Thu</th>
<th class="text-left">Fri </th>
<th class="text-left">Sat </th>
</tr>
</thead>
<tbody>
<tr>
<!-- response of ajax fetchSchedule is displayed here -->
</tr>
</tbody>
</template>
</v-simple-table>
...
</v-card>
</v-col>
</v-row>
</v-container>
</v-card>
...
</template>
<script>
...
export default {
data: () => ({
...,
date: new Date().toISOString().substr(0, 10),
modal: false,
}),
computed: {
...mapGetters(["dataDoctor","dataSchedule"])
},
methods: {
...mapActions([
"fetchDoctor",
"fetchSchedule"
]),
searchDoctor() {
...
this.fetchDoctor(params);
},
getScedule(doctorId) {
this.fetchSchedule(doctorId)
}
},
};
</script>
I make it using vuetify
when the searchDoctor method is called, it will call ajax fetchDoctor and get a response. the results will be stored in DataDoctor and displayed in a loop. This code has worked because it successfully displayed a list of doctors
my problem is I want to display the schedule of each doctor on the list. so I need to call ajax on each loop. then send it to the getScedule method to call the ajax getScedule and get a response. After that it can be displayed in the table
how do i do it? can i call ajax on each loop? if it can, how do I do that.? I have searched for references, but I did not find that
If fetchSchedule returns table data in html, you can likely do something like this:
<v-simple-table>
<template v-slot:default>
<thead>
<tr>
<th class="text-left">Sun</th>
<th class="text-left">Mon </th>
<th class="text-left">Tue</th>
<th class="text-left">Wed </th>
<th class="text-left">Thu</th>
<th class="text-left">Fri </th>
<th class="text-left">Sat </th>
</tr>
</thead>
<tbody>
<tr v-html="fetchSchedule(item.doctorId)"></tr>
</tbody>
</template>
</v-simple-table>
v-html will output the result of fetchSchedule inside the table row.

Conditional #click for <th> in VueJS

How can I conditionally enable/disable #click with v-if/v-else while iterating through with v-for on a <th>?
I have the following code:
<template>
<div id="TableHeaderDiv" class="TableHeaderClass">
<table cellpadding="0" cellspacing="0" border="0">
<thead>
<tr>
<th
v-for="column in columns"
v-bind:key="column.DataField"
#click="sortBy(column.DataField)"
:class="{ active: sortKey == column.DataField }"
:style="{ width: column.Width }"
>
{{ column.DisplayText }}
<div v-if="!column.isControl">
<span
class="arrow"
:class="sortOrders[column.DataField] > 0 ? 'asc' : 'dsc'"
></span>
</div>
</th>
</tr>
</thead>
</table>
</div>
</template>
I would like to write a condition so that v-if="!column.isControl" ... add the #click to the <th>.
My initial approach was to do something like this:
<template>
<div id="TableHeaderDiv" class="TableHeaderClass">
<table cellpadding="0" cellspacing="0" border="0">
<thead>
<tr>
<div v-for="column in columns" v-bind:key="column">
<th
v-if="!column.isControl"
#click="sortBy(column.DataField)"
:class="{ active: sortKey == column.DataField }"
:style="{ width: column.Width }"
>
{{ column.DisplayText }}
<div v-if="!column.isControl">
<span
class="arrow"
:class="sortOrders[column.DataField] > 0 ? 'asc' : 'dsc'"
></span>
</div>
</th>
<th v-else :style="{ width: column.Width }">
{{ column.DisplayText }}
</th>
</div>
</tr>
</thead>
</table>
</div>
</template>
But this just created a handful of <div> elements which is not what I intend to do.
Perhaps I am missing a fundamental concept with conditional rendering. Any help is much appreciated!
If you are using Vue.js 2.6 or higher you can pass null to the #click directive, and thus click listener will not be bound.
More details can be found in this RFC. Conveniently, if the argument
value is null, the binding/listener will be removed.
So your code could look something like this:
v-on="{ [!colum.isControl ? 'click' : null]: sortBy(column.DataField) }"