Based on the following tutorial:
https://youtu.be/4deVCNJq3qc?t=7710
Question> I am able to show the table as expected. However, the table column name doesn't show a hyper-link. What should I do to fix the issue?
Thank you
"dependencies": {
"bootstrap-vue": "^2.11.0",
"core-js": "^3.6.4",
"vue": "^2.6.11",
"vue-router": "^3.1.6",
"vuex": "^3.1.3"
},
<template>
<div>
<h1>Cats for Adoption</h1>
<b-table striped hover :items="cats">
<template slot="name" slot-scope="data">
<router-link :to="`/pets/${data.value}`">{{ data.value }}</router-link>
</template>
</b-table>
</div>
</template>
<script>
import cats from "#/data/cats";
export default {
data() {
return { cats: cats };
}
};
</script>
# /data/cats.js
export default [
{
name: "Fish",
breed: "tuxedo",
species: "cat",
gender: "male",
age: 20,
color: "black/white",
weight: 13,
location: "fourside",
notes: "Sweet kitty. He loves getting his belly rubbed."
}
];
Bootstrap-Vue appears to have changed the way you do custom data rendering. Your template should be:
<b-table striped hover :items="cats">
<template v-slot:cell(name)="data">
<router-link :to="`/pets/${data.value}`">{{ data.value }}</router-link>
</template>
</b-table>
CodeSandbox demo
Related
In my Table component, I'm using Vue Bootstrap's b-table component to create a table, which retrieves its' data from an external JSON file through Vuex. Now I also have another component, Actions, which is rendered on each row of the table. This component contains an edit button which is supposed to open a modal when clicked.
The problem is that whenever I click the edit button, 4 modals come up one on top of another. The issue seems to lie in the number of rows rendered, because in the JSON file, there are 4 objects, each of which contains the student's name, date of birth and so on. When I get rid of three of these objects, the modal only renders once. My conclusion is that the modal is rendering 4 times, for each row, but I have no idea how to fix this.
Here's the Table and Actions component:
<script>
import Actions from "./Actions.vue"
export default {
data() {
return {
fields: [
'index',
'full_name',
{ key: "date_of_birth", label: 'Date of Birth' },
'municipality',
{ key: "action", label: 'Action' }
],
// tableItems: this.$store.state.registeredStudents.registeredStudents
}
},
components: {
Actions
},
methods: {
generateIndex() {
return Math.floor(1000000 * Math.random()).toString().slice(0, 6);
}
},
computed: {
rows() {
return this.tableItems.length
},
tableItems() {
const registeredStudents = this.$store.state.registeredStudents.registeredStudents
return registeredStudents.map(student => ({
index: this.generateIndex(), ...student
}))
}
},
}
</script>
<template>
<b-table :fields="fields" :items="tableItems" :per-page="perPage" :current-page="currentPage" responsive="sm" primary-key="index"
striped hover>
<template #cell(action)="data">
<Actions/>
</template>
</b-table>
</template>
<script>
import { BIconPencilFill, BIconTrashFill } from 'bootstrap-vue';
export default {
}
</script>
<template>
<div>
<b-button variant="primary" class="mx-1 p-1" v-b-modal.edit-student>
<b-icon-pencil-fill></b-icon-pencil-fill>
</b-button>
<b-modal id="edit-student" title="Edit student info">
<p class="my-4">Hello from modal!</p>
</b-modal>
<b-button variant="danger" class="mx-1 p-1">
<b-icon-trash-fill></b-icon-trash-fill>
</b-button>
</div>
</template>
The problem here is most likely because your id's are not unique. For each row of your table you are generating the following;
<b-modal id="edit-student" title="Edit student info">
<p class="my-4">Hello from modal!</p>
</b-modal>
with the same ID.
I think the best approach here would be to include the Actions in the Table component. You don't need the extra component since it has no internal logic or props.
Table.vue
data() {
return {
fields: [
'index',
'full_name',
{ key: "date_of_birth", label: 'Date of Birth' },
'municipality',
{ key: "action", label: 'Action' }
],
lastClickedRowIndex: -1
}
},
...
<template>
<b-table :fields="fields" :items="tableItems" :per-page="perPage" :current-page="currentPage" responsive="sm" primary-key="index"
striped hover>
<template #cell(action)="data">
<b-button variant="primary" class="mx-1 p-1" #click="() => (lastClickedRowIndex = data.index)" v-b-modal.edit-student>
<b-icon-pencil-fill></b-icon-pencil-fill>
</b-button>
<b-button variant="danger" class="mx-1 p-1">
<b-icon-trash-fill></b-icon-trash-fill>
</b-button>
</template>
</b-table>
<b-modal id="edit-student" title="Edit student info">
<p class="my-4">Hello from modal!</p>
<!-- Dynamically change contents here based on lastClickedRowIndex -->
</b-modal>
</template>
I apologise if any syntax is incorrect I've only used Vue 3.
I am new in vue 3. I am trying to use vuetify autocomplete feature for my project. I have consulted official doc of vuetify . Autocomplete Showing [object Object]. Thanks in advance.
<script>
import { Form, Field } from 'vee-validate';
export default {
components: {
Form,
Field,
},
setup(props) {
},
data() {
return {
add: {
id: 1,
},
states: [
{ name: 'Florida', id: 1 },
{ name: 'Georgia', id: 2 },
{ name: 'Nebraska', id: 3 },
{ name: 'California', id: 4 },
{ name: 'New York', id: 5 },
],
};
},
methods: {
},
};
</script>
<template>
<v-row>
<v-autocomplete
v-model="add.id"
:items="states"
item-text="name"
item-value="id"
chips
deletable-chips
filled
></v-autocomplete>
</v-row>
</template>
How to show state name instead of [object object]
If you're using Vuetify 3, you should use "item-title" instead of "item-text".
And i think that Vuetify 2.6 is not compatible with Vue 3.
Give a feedback if this worked for you or not.
use :items="states.map(x=> x.name)" you need to use a string array.
Add return-object in v-autocomplete
<v-autocomplete
v-model="add.id"
:items="states"
item-text="name"
item-value="id"
chips
deletable-chips
filled
return-object
></v-autocomplete>
First of all, you mentioned the wrong reference URL of Vuetify (or maybe you are using the wrong version) because Vuetify 2 is not compatible with Vue 3. So, use Vuetify 3 for your development.
Second, if you don't want to select multiple items (because you didn't use multiple props) then the use of the chips prop doesn't seem worth it. However, if you want to use it, fix two syntax mistakes first-
Use closable-chips instead of deletable-chips (it is Vuetify 2 syntax.).
Use variant="filled" instead of filled (it is also Vuetify 2 syntax.)
Now, the reason autocomplete is showing object object is that you are using item-text which is Vuetify 2 belonging. Instead of that, you should use item-title and the issue will be fixed.
Here is the working demo of output when using item-text and when using item-title-
<!DOCTYPE html>
<html>
<head>
<link
href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css"
rel="stylesheet"
/>
<link
href="https://cdn.jsdelivr.net/npm/vuetify#3.0.5/dist/vuetify.min.css"
rel="stylesheet"
/>
</head>
<body>
<div id="app"></div>
<script src="https://unpkg.com/vue#3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#3.0.5/dist/vuetify.min.js"></script>
<script type="text/x-template" id="app-template">
<v-app>
<v-card flat>
<v-card-text>
<v-row no-gutters>
<v-col>
<h3 class="my-2">When using "item-text"</h3>
<v-autocomplete
v-model="add.id"
:items="states"
item-text="name"
item-value="id"
variant="filled"
chips
closable-chips
></v-autocomplete>
</v-col>
<v-col class="ms-3">
<h3 class="my-2">When using "item-title"</h3>
<v-autocomplete
v-model="add.id"
:items="states"
item-title="name"
item-value="id"
variant="filled"
chips
closable-chips
></v-autocomplete>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-app>
</script>
<script>
const { createApp } = Vue;
const { createVuetify } = Vuetify;
const vuetify = createVuetify();
const app = createApp({
template: "#app-template",
data() {
return {
add: {
id: 1,
},
states: [
{ name: 'Florida', id: 1 },
{ name: 'Georgia', id: 2 },
{ name: 'Nebraska', id: 3 },
{ name: 'California', id: 4 },
{ name: 'New York', id: 5 },
],
};
},
})
.use(vuetify)
.mount("#app");
</script>
</body>
</html>
So I have a datatable of images that I want to expand in an overlay tag on click.
To do that, I created an array for each column of the table and I mapped it to its corresponding image.
Here's the code :
<template>
<v-app>
<app-navbar />
<v-main>
<div class="text-center">
<h3>
test {{ $route.params.name }}, {{ $route.query.status }},{{
$route.query.tag
}}
</h3>
</div>
<v-data-table
:headers="headers"
:items="imagesref"
:items-per-page="5"
class="elevation-1"
>
<template v-slot:[`item.index`]="{ index }">
{{index+1}}
</template>
<template v-slot:[`item.ref`]="{ index }">
<v-img :src="imagesref[index]" max-width="750" max-height="750" #click="expref[index] = !expref[index]"/>
<v-overlay :value="expref[index]"><v-img :src="imagesref[index]" max-width="1300" max-height="900" #click="expref[index] = !expref[index]"/> </v-overlay>
</template>
<template v-slot:[`item.test`]="{ index }">
<v-img :src="imagestest[index]" max-width="750" max-height="750" #click="exptest[index] = !exptest[index]"/>
<v-overlay :value="exptest[index]"><v-img :src="imagestest[index]" max-width="1300" max-height="900" #click="exptest[index] = !exptest[index]"/> </v-overlay>
</template>
<template v-slot:[`item.res`]="{ index }">
<v-img :src="imagesresult[index]" max-width="750" max-height="750" #click="expres[index] = !expres[index]"/>
<v-overlay :value="expres[index]"><v-img :src="imagesresult[index]" max-width="1300" max-height="900" #click="expres[index] = !expres[index]"/> </v-overlay>
</template>
<template #[`item.Scrubber`]="{ index }">
<nuxt-link :to="{ path: 'scrubber', query: { imageref: imagesref[index],imagetest:imagestest[index],imageres:imagesresult[index] }}">Show Scrubber</nuxt-link>
</template>
</v-data-table>
</v-main>
</v-app>
</template>
<script>
import appNavbar from "../../../components/appNavbar.vue"
import axios from "axios"
export default {
components: { appNavbar },
name: "App",
data() {
return {
expref:[],
exptest:[],
expres:[],
items: [],
imagesref: [],
imagestest: [],
imagesresult: [],
headers: [
{ text: 'index',value: 'index',sortable:false},
{ text: 'Imagesref', value: 'ref',sortable:false },
{ text: 'Imagestest', value: 'test',sortable:false },
{ text: 'Imagesresult', value: 'res',sortable:false },
{ text: 'Scrubber', value: 'Scrubber',sortable:false },
]
}
},
async created() {
try {
const res = await axios.get(`http://localhost:3004/tests`, {
params: { name: this.$route.params.name },
})
this.items = res.data
this.imagesref = res.data[0].refimages
this.imagestest = res.data[0].testimages
this.imagesresult = res.data[0].resultimages
for (let i of this.imagesref){
this.expref.push(false);
this.exptest.push(false);
this.expres.push(false);
}
} catch (error) {
console.log(error)
}
}
}
</script>
<style scoped>
</style>
When I tested it, after I click on the image the corresponding variable in the array changes its value to true but the overlay is not getting displayed but somehow when I change the value manually on devtools it works.Does someone have any idea what's going on and how can i make it work ?
How can I show a custom component- BaseButton.vue in the rows of the table, if table component and button are common components?
I made the component BaseTable.vue. I use this component for all tables.
<template>
<b-table :fields="fields" :items="items"></b-table>
</template>
<script>
export default {
props: {
fields:{
type: Array,
require: true
},
items:{
type: Array,
require: true
},
}
fields - object in a parent component.
items - object from API.
Parent component (base button doesn't appear in table on page):
<template>
<base-table :fields="fields" :items="items">
<template #cell(actions)>
<base-button></base-button>
</template>
<base-table>
</template>
<script>
import BaseButton from '...';
export default {
data() {
return {
fields: [
{ key: 'Caption', label: 'Name' },
{ key: 'PreviousMonthCounter', label: 'Prev. month' },
{ key: 'CurrentMonthCounter', label: 'Curr. month' },
{ key: 'actions', label: 'Action' }
],
items: []
}
},
components: {
BaseButton
}
}
And now I need to render BaseButton.vue in the last column of the table and I can't to make it - table has only text fields from items.
Need to create a child component (BaseTable):
<template>
<b-table :items="items" :fields="fields">
<slot v-for="slot in Object.keys($slots)" :name="slot" :slot="slot" />
<template v-for="slot in Object.keys($scopedSlots)" :slot="slot" slot-scope="scope">
<slot :name="slot" v-bind="scope"></slot>
</template>
</b-table>
</template>
And use it in the parent component like , but must be replaced by your component name.
For example:
<base-table :fields="fields" :items="items">
<template #cell(nameOfTheField)="{item}">
{{ item.key }}
</template>
<template #cell(nameOfTheField)>
<base-button></base-button>
</template>
I have two components one is a Parent is some page random, and children is a component will use and more components for grids.
Parent
<template>
...
<DataTable
:items="items"
:fields="fields"
:currentPage="currentPage"
:perPage="perPage"
:filter="filter"
:sortBy="sortBy"
:sortDesc="sortDesc"
:sortDirection="sortDirection">
</DataTable>
...
</template>
<script>
import DataTable from "./DataTable.vue";
components: {
DataTable,
},
data: function(){
return {
fields: [],
items: [],
currentPage: 1,
perPage: 2,
filter: null,
sortBy: null,
sortDesc: false,
sortDirection: 'asc',
}
}
</script>
Child
<template>
...
<b-table
show-empty
stacked="md"
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
:sort-by="sortBy"
:sort-desc="sortDesc"
:sort-direction="sortDirection">
</b-table>
<b-row>
<b-col md="6" class="my-1">
<b-pagination
:total-rows="items.length"
:per-page="perPage"
v-model="currentPageComputed"
class="my-0" />
</b-col>
</b-row>
...
</template>
<script>
props: {
items: Array,
fields: Array,
currentPage: Number,
perPage: Number,
filter: String,
sortBy: String,
sortDesc: Boolean,
sortDirection: String,
},
computed: {
currentPageComputed: {
get () {
return this.$props.currentPage
},
set (i) {
this.$props.currentPage = i;
}
},
},
</script>
Finally looks similar to:
when use the pagination for change of page; works But, send me this error:
I read this problem is referent to "props in Vue is an anti-pattern".
So, how a fix?
The simplest solution is to to use the #input event of b-pagination to emit the changed value from child to parent:
<b-pagination
:total-rows="items.length"
:per-page="perPage"
:value="currentPage"
#input='emitPageValue'
class="my-0" />
</b-col>
And in methods:
methods: {
emitPageValue(value){
this.$emit('update:current-page', value)
}
}
Then, in parent you have to accept the changed value by applying the modifier .sync to the prop, so it will also handle the #update event:
<template>
...
<DataTable
:items="items"
:fields="fields"
:current-page.sync="currentPage"
:per-page="perPage"
:filter="filter"
:sort-by="sortBy"
:sort-desc="sortDesc"
:sort-direction="sortDirection">
</DataTable>
...
</template>
NB: Also pay attention to naming convention for props in template. It is recommended to use kebab-case for props in template and access the same property in camelCase in Javascript.