Rest API to v-data-table using v-select but v-data-table can't show so I use v-for to - vue.js

Purpose:
1.Rest API >>> by axios
2.Select category >>> by v-select
3.Show table >>> by v-data-table but i can't show table so I use v-for to show but I got this
here what I got
it show many table (according to the number of data sets)
Here is my code
HTML Part
<template>
<v-container>
<h1>JSON Data</h1>
<span>Data Test Table For Current Sensor Table</span>
<!-- Choose -->
<div id="top">
<!-- left-right -->
<v-row align-center>
<!-- title -->
<v-col cols="5">
<v-subheader>Choose Generation: {{ selected }}</v-subheader>
</v-col>
<!-- dropdown -->
<v-col cols="7">
<v-select :items="items" v-model="selected" dense></v-select>
</v-col>
</v-row>
</div>
<!-- Button -->
<v-btn block v-on:click="getData()">Load Data</v-btn>
<div v-for="(item, i) in data" :key="i" >
<v-data-table :items="data" :headers="headers" :items-per-page="5">
<template slot="data" slot-scope="props">
<td>{{ props.data.userId }}</td>
<td>{{ props.data.id }}</td>
<td>{{ props.data.title }}</td>
</template>
</v-data-table>
</div>
<!-- Test -->
<span class="data_id">***{{ data }} </span><br />
</v-container>
</template>
Script Part
<script>
import axios from "axios";
export default {
name: "Current",
data: () => ({
items: ["albums", "todos", "posts"],
selected: "",
headers: [
{ text: "USER_ID", align: "start", sortable: false, value: "userId" },
{ text: "ID", value: "id" },
{ text: "TITLE", value: "title" },
],
data: [],
}),
methods: {
getData() {
axios
.get("https://jsonplaceholder.typicode.com/users/1/" + this.selected)
.then((response) => {
this.data = response.data;
})
.catch((err) => alert(err));
},
},
mounted() {
this.getData();
},
};
</script>
Vue&Vuetify is new to me. If you have any suggestions, please tell me.
I try to remove div <div v-for="(item, i) in data" :key="i" ></div> but when I refresh this page it gone and show only empty table
show only empty table

Please remove v-for.
Because v-data-table displays table for data you passed, it will display same table as much as count of items in the data.
https://prnt.sc/1nsxc7w

Related

V-select issue in Vuetify 3

I'm using Vuetify 3.0.0-beta.0 ~ for my project (because it is the only version that supports vue3), and having a bit weird issue
I want to implement the same thing as described there https://codepen.io/reijnemans/pen/vYNadMo?editors=1010 with v-select involved, so I was needed to use Vuetify
copied snippet
<v-select
:items="items"
label="Standard"
>
<template v-slot:selection="{ item, index }">
<img :src="item.image">{{ item.name }}</template>
</template>
<template v-slot:item="{ item }">
<img :src="item.image">{{ item.name }}</template>
</v-select>
My Component:
<template>
<div class="resourceSelectors">
<v-col cols="10" lg="4" class="mx-auto">
<div class="text-center">
<h2 class="indigo--text" style="margin-bottom: 30px">Some Test H2</h2>
</div>
<v-col class="d-flex" cols="12" sm="6">
<v-select
:items="items"
label="Standard">
<template v-slot:selection="{ item }">
<img :src="item.image">{{ item.name }}
</template>
<template v-slot:item="{ item }">
<img :src="item.image">{{ item.name }}
</template>
</v-select>
</v-col>
</v-col>
</div>
</template>
<script>
import { mapState } from "vuex";
/* eslint-disable */
export default {
name: "testComponent",
data() {
return {
// hardware Configuration Validation Rules
items: [
{ name: 'Foo', image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'},
{ name: 'Bar', image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'},
{ name: 'Hoo', image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'},
{ name: 'Coo', image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'}],
}
}}
When I'm trying to run the above component I always get this weird error Failed setting prop "type" on <select>: value text is invalid. TypeError: Cannot set property type of #<HTMLSelectElement> which has only a getter,
Did anyone faced similar issue before?
In Vuetify 3, you need some workarounds to style the items in v-select, because the item slot resets the entire styling.
You should use the menu-props, with it you can pass props through to the v-menu component. It accepts an object with anything from /api/v-menu. This allows you to close the field on click.
In the item slot, you should use a v-list-item with an #click property to set the model.
I made an example here with a selection of symbols:
<script setup>
const symbols = [
'ab-testing',
'abacus',
'account',
'account-alert',
]
const form = { symbol: '', }
</script>
<template>
<v-select
v-model="form.symbol"
:items="symbols"
label="Symbol"
:prepend-inner-icon="'mdi-'+form.symbol"
:menu-props="{
closeOnClick: true,
closeOnContentClick: true,
}"
>
<template v-slot:selection="{ item, index }">
{{ item.value }}
</template>
<template v-slot:item="{ item, index }">
<v-list-item
:title="item.title"
:prepend-icon="'mdi-'+item.title"
#click="form.symbol = item.title"
>
</v-list-item>
</template>
</v-select>
</template>
I hope it helps you.
I couldn't find correct solution but I just wanted to share what I did about scoped slot. I think we should use item.raw to access name and image. And the next problem is how to make it clickable to trigger select event that I didn't know yet :(
const { createApp } = Vue
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = createApp({
data() {
return {
value: null,
items: [
{
name: 'Foo',
image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'
},
{
name: 'Bar',
image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'
},
{
name: 'Hoo',
image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'
},
{
name: 'Coo',
image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'
}
]
}
}
});
app.use(vuetify).mount('#app');
<link href="https://cdn.jsdelivr.net/npm/vuetify#3.0.0-beta.9/dist/vuetify.min.css" rel="stylesheet"/>
<script src="https://unpkg.com/vue#3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#3.0.0-beta.9/dist/vuetify.min.js"></script>
<div id="app">
<div class="resourceSelectors">
<v-col cols="10" lg="4" class="mx-auto">
<div class="text-center">
<h2 class="indigo--text" style="margin-bottom: 30px">Some Test H2</h2>
</div>
<v-col class="d-flex" cols="12" sm="6">
<v-select
v-model="value"
:items="items"
item-title="name"
item-value="name"
label="Standard">
<template v-slot:item="{item}">
<v-list-item
:prepend-avatar="item.raw.image"
:title="item.raw.name"
/>
</template>
</v-select>
</v-col>
</v-col>
</div>
</div>

Change vuetify simple-table to data-table

I have this simple table in Vuetify, without headers and only one column. How can I change it to vuetify v-data-table?
<v-simple-table>
<thead />
<tbody>
<tr
v-for="item in categories"
:key="item"
#click="handleClick"
>
<td>{{ item }}</td>
<v-switch />
</tr>
</tbody>
</v-simple-table>
categories is a simple array of strings. I want to change it to data-table in order to nicely handle clicking and selecting rows.
Check this codesandbox I made: https://codesandbox.io/s/stack-71617004-simple-to-v-data-table-bm2yn1?file=/src/components/Example.vue
Using body slot
You can use the body slot of the data table and use almost the same code you have in your simple table like this. This way you set up the handleClick function in the tr:
<v-data-table
:headers="headers"
:items="items"
hide-default-footer
hide-default-header
:items-per-page="-1"
:footer-props="{
itemsPerPageOptions: [-1],
}"
>
<template v-slot:body="{ items}">
<tbody>
<tr v-for="item in items" :key="item" #click="handleClick(item)">
<td align="left">{{item}}</td>
</tr>
</tbody>
</template>
</v-data-table>
Using item slot
Or you can use the item slot, like this. In this other way, the handleClick function is configured using the #click:row event of the data table.
If you try to use the item slot with your array of strings, it will work but you'll get some warnings in your console. Telling you that your data-table item slot expected an object and received an string. That's because v-data-table component expects to receive an array of objects.
To avoid this warning you can simply convert your array of string into a dummy array of objects using Array.prototype.map, and bind the computed property instead.
computed: {
myItemsTransformed() {
return this.items.map(item => ({ name: item }));
}
},
<v-data-table
:headers="headers"
:items="myItemsTransformed"
hide-default-footer
hide-default-header
:items-per-page="-1"
:footer-props="{
itemsPerPageOptions: [-1],
}"
#click:row="(item) => handleClick(item.name)"
>
<template #item.name="{ item }">
{{ item.name }}
</template>
</v-data-table>
Notice that in both examples I have used the props hide-default-footer, hide-default-header to hide the footer and header of the data table and also set the items-per-page to -1. To show all the items of the table and avoid the pagination.
You can change like this :
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
categoryList: ['Category 1', 'Category 2', 'Category 3'],
}),
computed: {
categoriesHeader() {
return [
{ text: "Name", value: "name" }
];
},
},
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.5.0/dist/vuetify.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app id="a">
<v-data-table :headers="categoriesHeader" :items="categoryList" item-key="id" class="elevation-1">
<template v-slot:[`item.name`]="{ item }">
{{ item }}
</template>
</v-data-table>
</v-app>
</div>

Vuejs/Buetify : clear search filter using searchable prop of b-table

I'm using buefy to create a table with input filters in columns.
This is what it looks like:
<b-table
:data="cars"
:sticky-header="true"
:selected.sync="selected"
>
<template slot-scope="props">
<template v-for="column in columns">
<b-table-column :key="column.id" v-bind="column">
<template
v-if="column.searchable"
slot="searchable"
slot-scope="props"
>
<b-input
v-model="props.filters[props.column.field]"
placeholder="Search..."
icon="magnify"
size="is-small"
/>
</template>
{{ props.row[column.field] }}
</b-table-column>
</template>
</template>
</b-table>
...
...
data () {
return {
selected: null,
columns: [
{
field: 'constructor',
label: 'Constructor',
searchable: true
},
....
]
}
I would like to be able to clear the searched fields.
Any suggestions to achieve this please?
Your b-input is bound to props.filters[props.column.field], so it means that you should be able to add an icon to reset this value:
<b-input
v-model="props.filters[props.column.field]"
...
icon-right="close-circle"
icon-right-clickable
#icon-right-click="props.filters[props.column.field] = ''"
>
Please let me know if that works.

Vue-MultiSelect: Using two multiselects at once and how to hide options based on what is shown in other

I am using a plugin called Vue-MultiSelect and showing it twice in my component. The first multiselect has a label of "Add New Contacts" and the second multiselect has a label of "Current Listed Contacts".
How can I hide the users for the "Add New Contacts" multiselect dropdown if they are already listed in the "Current Listed Contacts"?
Screenshot of problem:
I am using props to show the users for the "Current Listed contacts" multiselect. Here's the code for my Single file component where these multiselects reside (also demo link: CodeSandBox Code Editor) Note: click on POSTS --> EDIT (Acme Widget) to see the page where I am talking about
<template>
<div>
<label>Add New Contacts:</label>
<multiselect
id="add_new_contacts_input"
v-model="NewContacts"
:options="options"
label="lastname"
placeholder="Select or search for an existing contact"
track-by="uid"
:loading="isLoading"
:custom-label="selectedNameLabel"
aria-describedby="searchHelpBlock"
selectLabel
:multiple="true"
>
<template
slot="singleLabel"
slot-scope="props"
>{{ props.option.lastname }}, {{props.option.firstname}}</template>
<template slot="option" slot-scope="props">
<strong>{{ props.option.lastname }}</strong>
, {{ props.option.firstname }} —
<small>{{ props.option.email }}</small>
</template>
</multiselect>
<!-- <small id="searchHelpBlock" class="form-text text-muted"><font-awesome-icon icon="exclamation-circle" /> If customer does not exist, you will be prompted to add a new customer</small> -->
<!-- <h3>New contacts to be added:</h3>
<ul>
<li v-for="value in values" :key="value.uid">{{value.lastname}}, {{value.firstname}}</li>
</ul>-->
<label>Current Listed Contacts</label>
<multiselect
id="current_listed_contacts_input"
v-model="CurrentListedValues"
placeholder="There are no current contacts"
label="lastname"
track-by="uid"
:options="options"
:multiple="true"
:custom-label="selectedNameLabel"
:loading="isLoading"
selectLabel
>
<!-- formatting for the drop down list -->
<template slot="option" slot-scope="props">
<strong>{{ props.option.lastname }}</strong>
, {{ props.option.firstname }} —
<small>{{ props.option.email }}</small>
</template>
</multiselect>
</div>
</template>
<script>
import Multiselect from "vue-multiselect";
// import ApiService from "#/apiService";
export default {
components: { Multiselect },
props: ["users", "contacts"],
data() {
return {
NewContacts: [],
CurrentListedValues: this.contacts,
options: this.users,
isLoading: true
};
},
created() {
this.isLoading = false;
},
methods: {
selectedNameLabel(option) {
return `${option.lastname}, ${option.firstname} -- ${option.email}`;
}
}
};
</script>
you can use computed to filter your list:
computed: {
addOptions() {
let opt = this.users;
this.CurrentListedValues.forEach(c => {
opt = opt.filter(i => i.uid !== c.uid);
});
return opt;
}
}
and in your select list change options to addOptions: :options="addOptions"

Condition on template with v-if using v-slot prop

I'm trying to make a condition to enable a named slot like this:
<template v-slot:item="{ item }" v-if="item.loading">
<v-progress-circular indeterminate color="primary"></v-progress-circular>
</template>
My use case is a Vuetify datatable: each item has a "loading" property, and I'd like to activate "item" slot only if the row is loading ("item" slot is Slot to replace the default rendering of a row)
The error is that item is undefined in the v-if, which seems logic : item is only defined for template children tag.
Is there a way to solve this problem?
You can filter the items that you pass to the datatable with a computed property.
Can you just not swap element based on loading ?
Vue.config.devtools = false;
Vue.config.productionTip = false;
var app = new Vue({
el: '#app',
data: {
items: [{data : "", loading: true}, {data : "Some data", loading: false}]
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="item in items">
<div>
<div v-if="item.loading">
Loading...
</div>
<div v-else>
{{item.data}}
</div>
</div>
</div>
</div>
I had a similar problem, and I solved it in Vuetify 2 by importing VDataTable/Row as 'v-data-table-row', and using it to render 'regular' table rows, and for custom rows I used my own template.
JavaScript
import Row from 'vuetify/lib/components/VDataTable/Row.js';
export default {
components: { 'v-data-table-row': Row },
data() {
return {
currentItemName: 'Ice cream sandwich'
}
}
// headers, items, etc...
}
HTML
<template v-slot:item="{ item }">
<tr v-if="item.name == currentItemName" class="blue-grey lighten-4">
<td>Custom prefix - {{ item.name }}</td>
<td colspan="2">{{ item.calories }} - Custom suffix</td>
</tr>
<v-data-table-row v-else :headers="headers" :item="item">
<template
v-for="(index, name) in $scopedSlots"
v-slot:[name.substr(5)]="data"
>
<slot
v-if="name.substr(0, 5) === 'item.'"
:name="name"
v-bind="data"
></slot>
</template>
</v-data-table-row> </template
You can check out working example here.
You can just put the v-if on the child element
<template #item="{ item }">
<v-progress-circular
v-if="item.loading"
color="primary"
indeterminate
></v-progress-circular>
</template>