Adding functionality to Vuetify component, mass pass in props - vue.js

I want to be able to send props to a Vuetify component without needing to assign each one within my component, is there a way I can just mass pass in all of the props?
Below is what i'm currently doing, however there's a lot of prop'.
I have attempted to simply extend the VSelect component, however this returns multiple errors which don't seem simple to fix!
<template>
<v-flex xs12 sm6>
<v-select v-model="selected" :items="data"
:label="label"
:multiple="multiple"
:chips="chips"
:hint="hint"
:persistent-hint="persistentHint"
:counter="counter"
:dark="dark"
></v-select>
</v-flex>
</template>
<script>
export default {
props: {
label: {
default: false,
type: String|Boolean
},
multiple: {
default: true,
type: Boolean
},
chips: {
default: true,
type: Boolean
},
hint: {
default: '',
type: String|Boolean
},
persistentHint: {
default: this.hint !== '' || this.hint !== false,
type: String|Boolean
},
counter: {
default: false,
type: Number|Boolean
},
dark: {
default: false,
type: Boolean
},
},
data: function() {
return {
selected: [ ],
data: [
'test', 'test2', 'test3'
]
}
}
}
</script>

You can pass props as an object:
<v-select
v-model="selected"
:items="data"
v-bind="$props"
></v-select>
[ https://v2.vuejs.org/v2/guide/components-props.html#Passing-the-Properties-of-an-Object ]

Related

How to clear v-date-picker without using cleearable

I'm using vuetify and nuxt.js to make forms with some text fields and date pickers.
this is one of the child component.
<template>
<v-menu
v-model="menu"
:close-on-content-click="false"
>
<template #activator="{ on }">
<v-text-field
v-model="date"
outlined
readonly
v-on="on"
/>
</template>
<v-date-picker
v-model="date"
:day-format="(date) => new Date(date).getDate()"
></v-date-picker>
</v-menu>
</template>
<script>
export default {
props: {
value: { type: String, default: '' },
placeholder: { type: String, default: '' },
},
data() {
return {
menu: false,
selectedDate: this.value,
}
},
computed: {
date: {
get() {
return this.selectedDate
},
set(date) {
this.selectedDate = date
this.$emit('input', date)
this.menu = false
},
},
},
}
</script>
when the reset button which is on Parent Component clicked, empty string props to this component. selectedDate can be reset, but on the v-text-field , there is data still.
How can I make this work properly?
You just need to add watcher like this:
watch: {
value: {
immediate: true,
deep: true,
handler(newValue) {
this.selectedDate = newValue
}
}
},

Search in vuetify datatable (server side)

I'm using vuetify datatables with server side in my app.
Pagination and sorting works fine but I have problem with search input - when I write something in input, new requests dont't send to api.
I found solution with pagination.sync in options but when I try it I get an error in the console:
[BREAKING] 'pagination' has been removed, use 'options' instead. For
more information, see the upgrade guide
https://github.com/vuetifyjs/vuetify/releases/tag/v2.0.0#user-content-upgrade-guide
My code is here:
<template>
<v-container fluid>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
></v-text-field>
<v-data-table
:items="usersList"
:headers="headers"
sort-by="name"
:sort-desc="false"
:options.sync="options"
:server-items-length="totalUsers"
:search="search"
:loading="loading"
loading-text="Ładowanie..."
class="elevation-0"
></v-data-table>
</v-container>
</template>
<script>
export default {
data: () => ({
totalUsers: 0,
usersList: [],
search: "",
loading: true,
options: {},
headers: [
{ text: 'Nazwa użytkownika', value: 'name' },
{ text: 'Adres e-mail', value: 'email' },
{ text: 'Opcje', value: 'actions', sortable: false },
]
}),
watch: {
options: {
handler () {
this.getUsers();
},
deep: true
},
},
mounted () {
this.getUsers();
},
methods: {
getUsers() {
this.loading = true;
userService.getAll(this.options).then(data => {
if (data.status == "success") {
this.usersList = data.items;
this.totalUsers = data.total;
} else {
console.log("Nie udało się pobrać użytkowników.");
}
this.loading = false;
});
},
}
};
</script>

nuxt.js fetching data to vuetify table

i am facing problem on fetching data inside vuetify table, it's not showing any data in side table.
MY Laravel API
Route::get('/businesslist',
'BusinessController#userlist')->name('businesslist');
Laravel API Controller
public function businesslist() {
$businesslist = Business::paginate(2)->toJson(JSON_PRETTY_PRINT);
return response($businesslist);
}
}
Nuxt Code
<template>
<v-card>
<v-card-title>
Nutrition
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="mdi-magnify"
label="Search"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
:headers="headers"
:items="registerlist"
:search="search"
></v-data-table>
</v-card>
</template>
<script>
export default {
data () {
return {
search: '',
headers: [
{ text: 'SL,No', value: '' },
{ text: 'Name', value: 'name' },
{ text: 'Mobile ', value: 'mobile_number' },
{ text: 'Location ', value: 'location' },
{ text: 'Join Date ', value: 'registration_date' },
{ text: 'Renewal Date ', value: 'registration_renewal_date' },
],
registerlist: [
],
}
axios.get('/Businessregisterlist')
.then(res => this.registerlist = res.data.registerlist)
},
}
</script>
Firstly, you have to call API within a Function like this:
<script>
export default {
data() {
return {
list: []
}
},
methods: {
callAPI() {
axios.get('/', then(function(res){
this.list = res.data.registerList;
}))
}
},
mounted(){
this.callAPI(); // if you need that List on load then you can use mounted() otherwise that function will call during event.
}
}
</script>
Here, inside then() I think it's better to use function(). Because if you use function then it will indicate this Object. So, we can easily access all the Object Properties (Like: data(), methods() etc.).

Vue: Accessing prop to set class?

I am using a pagination library ( https://github.com/arnedesmedt/vue-ads-pagination ) and the VueAdsPageButton has a hidden prop on it called active that is a boolean value depending on whether or not the button is active. I am trying to set the id based on whether or not the active prop is true so I can style it accordingly. I tried:
v-bind:id="{ selected: active} but I get the warning that active is referenced in the render but doesn't exist. Not sure what I am doing wrong here.
This is my code below:
<VueAdsPagination
:total-items="totalOrdersNumber ? totalOrdersNumber : 0"
:page="page"
:loading="loading"
:items-per-page="10"
:max-visible-pages="10"
#page-change="pageChange"
#range-change="rangeChange"
>
<template
slot="buttons"
slot-scope="props"
>
<VueAdsPageButton
v-for="(button, key) in props.buttons"
v-bind:id="{ selected: active}"
:key="key"
:page="page"
v-bind="button"
#page-change="page = button.page;"
/>
</template>
</VueAdsPagination>
EDIT:
here is the component code from the library for VueAdsPageButton
<template>
<button
:class="buttonClasses"
:disabled="disabled"
:title="title"
#click="pageChange"
>
<i
v-if="loading"
class="fa fa-spinner fa-spin"
/>
<span
v-else
v-html="html"
/>
</button>
</template>
<script>
export default {
name: 'VueAdsPageButton',
props: {
page: {
type: [
Number,
String,
],
required: true,
},
active: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
html: {
type: String,
required: true,
},
title: {
type: String,
default: '',
},
loading: {
type: Boolean,
default: false,
},
disableStyling: {
type: Boolean,
default: false,
},
},
computed: {
buttonClasses () {
if (this.disableStyling) {
return {};
}
return {
'focus:vue-ads-outline-none': true,
'vue-ads-ml-1': true,
'vue-ads-leading-normal': true,
'vue-ads-w-6': true,
'vue-ads-bg-teal-500': this.active,
'vue-ads-text-white': this.active,
'vue-ads-cursor-default': this.active || this.disabled,
'vue-ads-bg-gray-200': this.disabled && this.page !== '...',
'vue-ads-text-gray': this.disabled && this.page !== '...',
'hover:vue-ads-bg-gray-100': !this.active && !this.disabled,
};
},
},
methods: {
pageChange () {
if (
this.page === '...' ||
this.disabled ||
this.active
) {
return;
}
this.$emit('page-change');
},
},
};
</script>
You can bind custom id or class on active button like this:
<vue-ads-page-button
v-for="(button, key) in props.buttons"
:key="key"
v-bind="button"
:id="button.active ? 'some-id' : null"
:class="{'some-class': button.active}"
#page-change="page = button.page"
#range-change="start = button.start; end = button.end"
/>
Here is also JSFiddle from library documentation where you can also see this - Link

Vue - Update Data on Bootstrap Table Custom Component

I am attempting to make a custom component in Vue 2.0 that extends the existing functionality of the Bootstrap Vue library <b-table>. It mostly works how I would like it to except that the removeRow and resetData functions defined in the index.jsp don't work how I'd like them to.
removeRow does visually remove the row, and removes it from the data prop but the row reappears after the next interaction (sort, filter, etc.). So it's not actually updating the right thing. I'm trying to use a v-model as a shallow copy of items so that I can make deletions to it without affecting the original set of data but I'm just missing something.
resetData does set the data in the table back correctly, but doesn't re-render the table so you can't see the re-added rows, until you do a separate interaction (sort, filter, etc.), in which case they reappear.
So I know I'm somewhat close but would really appreciate any insight on how to get this working correctly and ways I could improve any part of this component.
OreillyTable.vue.js
const OreillyTable = {
inheritAttrs: false,
data: function () {
return {
filter: null,
sortDesc: false,
hideEmpty: false,
isBusy: false,
currentPage: 1,
data: null
}
},
mounted: function () {
let filtered = this.slots.filter(function(value, index, arr){
return value.customRender;
});
this.slots = filtered;
},
methods: {
oreillyTableSort (a, b, key) {
if (a[key] === null || b[key] === null) {
return a[key] === null && b[key] !== null ? -1 : (a[key] !== null && b[key] === null ? 1 : 0);
} else if (typeof a[key] === 'number' && typeof b[key] === 'number') {
// If both compared fields are native numbers
return a[key] < b[key] ? -1 : (a[key] > b[key] ? 1 : 0)
} else {
// Stringify the field data and use String.localeCompare
return this.toString(a[key]).localeCompare(this.toString(b[key]), undefined, {
numeric: true
});
}
},
toString (val) {
return typeof val !== "undefined" && val != null ? val.toString() : '';
},
oreillyFilter (filteredItems) {
this.totalRows = filteredItems.length;
this.currentPage = 1;
}
},
props: {
fields: {
type: Array
},
items: {
type: Array,
required: true
},
hideEmpty: {
type: Boolean
},
filterPlaceholder: {
type: String,
default: "Filter"
},
sortFunction: {
type: Function,
default: null
},
filterFunction: {
type: Function,
default: null
},
slots: {
type: Array
},
sortBy: {
type: String,
default: null
},
perPage: {
type: Number,
default: 10
},
value: {
}
},
template: `<div :class="{ 'no-events' : isBusy }">
<b-row>
<b-col md="12">
<b-form-group class="mb-2 col-md-3 float-right pr-0">
<b-input-group>
<b-form-input v-model="filter" :placeholder="filterPlaceholder" class="form-control" />
</b-input-group>
</b-form-group>
</b-col>
</b-row>
<div class="position-relative">
<div v-if="isBusy" class="loader"></div>
<b-table stacked="md" outlined responsive striped hover
v-bind="$attrs"
v-model="data"
:show-empty="!hideEmpty"
:items="items"
:fields="fields"
:no-provider-sorting="true"
:no-sort-reset="true"
:filter="filter"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
:sort-compare="sortFunction === null ? this.oreillyTableSort : sortFunction"
:busy.sync="isBusy"
:current-page="currentPage"
:per-page="perPage"
#filtered="filterFunction === null ? this.oreillyFilter : filterFunction">
<template :slot="slot.key" slot-scope="row" v-for="slot in slots">
<slot :name="slot.key" :data="row"></slot>
</template>
</b-table>
<b-row v-if="items.length > perPage">
<b-col sm="12">
<b-pagination size="md" :total-rows="items.length" v-model="currentPage" :per-page="perPage"></b-pagination>
</b-col>
</b-row>
</div>
</div>`
};
index.jsp
<script>
Vue.use(window.vuelidate.default);
Vue.component('oreilly-table', OreillyTable);
const dashboardItems = [
{ id: 12, firstName: "John", lastName: "Adams", tmNumber: "588999", team: "Corporate", flapjackCount: 4, enrollDate: "2018-11-05" },
{ id: 13, firstName: "George", lastName: "Washington", tmNumber: "422111", team: "America", flapjackCount: 28, enrollDate: "2018-10-01" },
{ id: 14, firstName: "Abraham", lastName: "Lincoln", tmNumber: "358789", team: "America", flapjackCount: 16, enrollDate: "2017-09-02" },
{ id: 15, firstName: "Jimmy", lastName: "Carter", tmNumber: "225763", team: "Core", flapjackCount: 9, enrollDate: "2018-03-02" },
{ id: 16, firstName: "Thomas", lastName: "Jefferson", tmNumber: "169796", team: "Core", flapjackCount: 14, enrollDate: "2018-05-01" }
];
const Dashboard = {
template: `<jsp:include page="dashboard.jsp"/>`,
data: function(){
return {
notification: {
text: "The Great Flapjack Contest will be held on December 25, 2018.",
variant: "primary",
timer: true
},
fields: [
{ key: "name", label: "Name", sortable: true, customRender: true },
{ key: "team", label: "Team", sortable: true },
{ key: "enrollDate", label: "Enrollment Date", sortable: true, formatter: (value) => {return new Date(value).toLocaleDateString();} },
{ key: "flapjackCount", sortable: true },
{ key: "id", label: "", 'class': 'text-center', customRender: true }
]
}
},
methods: {
removeRow: function(id) {
this.$refs.table.isBusy = true;
setTimeout(() => { console.log("Ajax Request Here"); this.$refs.table.isBusy = false; }, 1000);
const index = this.$refs.table.data.findIndex(item => item.id === id)
if (~index)
this.$refs.table.data.splice(index, 1)
},
resetData: function() {
this.$refs.table.data = dashboardItems;
}
}
};
const router = new VueRouter({
mode: 'history',
base: "/ProjectTemplate/flapjack",
routes: [
{ path: '/enroll', component: Enroll },
{ path: '/', component: Dashboard },
{ path: '/404', component: NotFound },
{ path: '*', redirect: '/404' }
]
});
new Vue({router}).$mount('#app');
dashboard.jsp
<compress:html>
<div>
<oreilly-table ref="table" :items="dashboardItems" :slots="fields" :fields="fields">
<template slot="name" slot-scope="row">
{{ row.data.item.firstName }} {{ row.data.item.lastName }} ({{ row.data.item.tmNumber }})
</template>
<template slot="id" slot-scope="row">
Remove
</template>
</oreilly-table>
<footer class="footer position-sticky fixed-bottom bg-light">
<div class="container text-center">
<router-link tag="button" class="btn btn-outline-secondary" id="button" to="/enroll">Enroll</router-link>
 
<b-button #click.prevent="resetData" size="md" variant="outline-danger">Reset</b-button>
</div>
</footer>
</div>
I tried to reproduce your problem with a simple example (see: https://codesandbox.io/s/m30wqm0xk8?module=%2Fsrc%2Fcomponents%2FGridTest.vue) and I came across the same problem you have. Just like the others already said, I agree that the easiest way to reset original data is to make a copy. I wrote two methods to remove and reset data.
methods: {
removeRow(id) {
const index = this.records.findIndex(item => item.index === id);
this.records.splice(index, 1);
},
resetData() {
this.records = this.copyOfOrigin.slice(0);
}
}
On mount I execute a function that makes a copy of the data. This is done with slice because otherwise it makes only a reference to the original array (note that normally JS is pass-by-value, but as stated in the vue documentation with objects, and thus internally in vue it is pass by reference (see: Vue docs scroll to the red marked text)).
mounted: function() {
this.copyOfOrigin = this.records.slice(0);
}
Now you can simple remove a record but also reset all the data.
SEE FULL DEMO
I hope this fixes your issue and if you have any questions, feel free to ask.