Why aren't images being displayed in my v-data-table - vue.js

So currently I am using v-data-table to display the data being passed into the vue component via a prop. The prop is an array of objects, each object has several fields but the only fields I want to display are name, img, and store.price. Name and price display perfectly fine, but when I try to display an image, only the image link appears in the data table. Can someone take a look at my code and steer me in the right direction?
<template>
<v-data-table
v-model="selected"
:headers="headers"
:items="displayedTools"
:items-per-page="10"
:single-select="singleSelect"
show-select
class="elevation-1 tool-table"
>
<template slot="items" slot-scope="props">
<td><img :src="props.item.img" style="width: 10px; height: 10px"></td>
<td class="text-xs-right">{{ props.item.name }}</td>
<td class="text-xs-right">{{ props.item.stores.price }}</td>
</template>
</v-data-table>
</template>
<script>
export default {
name: 'ToolResults',
props: ['found_tools'],
data() {
return {
singleSelect: false,
selected: [],
headers: [
{
text: 'Image',
value: 'img'
},
{
text: 'Tool Name',
align: 'left',
sortable: true,
value: 'name',
},
{
text: 'Price',
value: 'stores.price'
}
],
displayedTools: [{}]
}
},
created() {
this.populateTable()
this.addImages()
},
methods: {
populateTable(){
this.found_tools.forEach(element => {
this.displayedTools.push(element);
});
},
//Method might need to be added to display Prices properly because it is an array.
displayPrices(){
},
//Add Image to rows
addImages(){
this.displayedTools.forEach(function(part, index, theArray) {
//theArray[index].img = "<v-img src=" + theArray[index].img + "><v-img>"
theArray[index].img = 'https:' + theArray[index].img
})
},
toToolboxPage(toolbox) {
console.log(toolbox)
// The Toolbox.vue compoent is already created. The user just needs to be redirected there
}
}
}
</script>
<style>
.tool-table {
margin: 2em;
}
</style>
The result of the above code is:

It seems that you haven't closed your img tag at the template.
Also in your addImages() method you have commented the line that assign the images and left the one that assigns a string and the v-img tag isn't closed.
So it should be something like:
addImages(){
this.displayedTools.forEach(function(part, index, theArray) {
theArray[index].img = "<v-img src=" + theArray[index].img + "></v-img>"
})
},

Related

vue2 list return mixed string or component

In loop like this, I mostly just iterate over item values as strings, but sometimes need to return rendered component, for example build link element, or dropdown menu, for that table cell - need to find a way to return other component output instead of raw html string
<tr class="listing-item listing-item-category">
<td v-for="td in headeritems">{{val(td.k)}}</td>
</tr>
Is that even possible? I've found no mention of this, how should the method code go to return other component output? I know I would have to use v-html, but how to get it?
Assume we have a list like this:
headerItems: [
{
type: 'text',
value: 'Some text'
},
{
type: 'img',
props: {
src: 'http://some-where....'
}
},
{
type: 'my-component',
value: 'v-model value',
props: {
prop1: 10,
prop2: 'Blah bla',
},
events: {
myEvt: () => console.log('myEvt has fired')
}
},
],
So, We can render it:
<tr>
<td
v-for="(item, i) in headerItems" :key="i"
>
<div v-if="item.type === 'text'"> {{ item.value }}</div>
<component
v-else
:is="item.type"
v-model="item.value"
v-bind="item.props"
v-on="item.events"
/>
</td>
</tr>

Image not showing in Vuetify data table slot

I am trying to display images in a Vuetify data table column, but the image is not displaying in the desired slot, and only shows the URL of the image, e.g. "9384053.jpg". How do I display an image using slots in Vuetify?
Segment is an array containing image URLs such as 93034.jpg, 9348903.jpg etc. I am trying to display the first image only e.g. Segment[0] with a sample output of 846454.jpg.
<v-card>
<v-card-title>
Animals
<v-spacer></v-spacer>
</v-card-title>
<v-data-table
:headers="headers"
:items="entries"
:items-per-page="12"
class="elevation-3"
:multi-sort="true"
mobile-breakpoint
:search="search"
>
<template v-slot:item.Image="{ item }">
<img :src="require(`${item.Image}`)" style="width: 50px; height: 50px" />
</template>
</v-data-table>
</v-card>
Here is the script file
<script>
import firebase from '../firebaseConfig';
const db = firebase.database().ref('/');
export default {
name: 'Animals',
data: () => ({
search: '',
entries: [],
headers: [
{ text: 'ID', value: 'ID' },
{ text: 'RFID', value: 'RFID' },
{ text: 'Image', value: 'Segment[0]' },
{ text: 'Age', value: 'Age Years' },
{ text: 'Weight', value: 'Weight' },
],
}),
methods: {
readAnimals() {
db.once('value', (snapshot) => {
snapshot.forEach((doc) => {
const dataRetrieve = doc.val();
this.$data.entries.push({
ID: dataRetrieve.ID,
RFID: dataRetrieve.RFID,
'Age Years': dataRetrieve['Age Years']
Weight: dataRetrieve.Weight,
Length_History: dataRetrieve['Length_History'],
Length: dataRetrieve.Length,
Tag: dataRetrieve.Tag,
Head: dataRetrieve.Head,
Segment: dataRetrieve.Segment,
});
});
return this.$data.entries;
}).catch((error) => {
console.log('error getting documents', error);
});
},
},
mounted() {
this.readAnimals();
},
};
</script>
<template v-slot:item.Image="{ item }">
<img :src="item.Image" style="width: 50px; height: 50px" />
</template>
You should bind it this way I think.
This seems to do the trick
<template v-slot:item.Segment[0]="{item}">
<img :src="item.Segment[0]" style="width: 50px; height: 50px" />
</template>
https://imgur.com/a/LX7JKoy

How to pull data from a Vuetify Datatable selected row

I have a Vuetify Datatable
<v-data-table
:headers="headers"
:items="members"
item-key="id"
v-model="selected"
:search="search"
>
<template slot="items" slot-scope="props">
<tr :active="props.selected" #click="select(props.item)">
<td>{{ props.item.name }}</td>
<td class="text-xs-right">{{ props.item.FirstName}}</td>
<td class="text-xs-right">{{ props.item.LastName }}</td>
<td class="text-xs-right">{{ props.item.email }}</td>
<td class="text-xs-right">{{ props.item.department}}</td>
<td class="text-xs-right">{{ props.item.division}}</td>
</tr>
</template>
And when I select a row I want to be able to populate an Item on the same page with some of the data such as the name and email in a v-card. I currently have
{{msg}}
and in my script I have
return {
msg: "",
then
select(selectedItem) {
this.selected = [];
this.members.forEach(item => {
if (item.id == selectedItem.id) {
this.selected.push(item);
this.msg = selectedItem.FirstName;
}
});
},
I need to put name into the msg. I feel that I'm going the long way around to get my data and was wondering if someone has a better solution. Thanks for the support.
<v-data-table #click:row="rowClick" item-key="name" single-select ...
methods: {
rowClick: function (item, row) {
row.select(true);
//item - selected item
}
}
<style>
tr.v-data-table__selected {
background: #7d92f5 !important;
}
</style>
or
<style scoped>
/deep/ tr.v-data-table__selected {
background: #7d92f5 !important;
}
</style>
Example
https://codepen.io/nicolai-nikolai/pen/GRgLpNY
Since there is a binding between the data table and this.selected, you only need to populate msg as a computed property of the component. You don't need to manually add to this.selected by listening to the click event.
computed: {
msg() {
const selectedRow = this.selected[0];
return selectedRow ? `${selectedRow.firstName} ${selectedRow.lastName}` : "no data selected";
}
}
EDIT
I've added a minimal example. Note for the item-key prop of v-data-table, you should use unique values.
<template>
<v-card>
<v-card-text>
<v-data-table :headers="headers" :items="members" v-model="selected" item-key="id">
<template slot="items" slot-scope="props">
<td>
<v-checkbox
v-model="props.selected"
:disabled="!props.selected && selected.length != 0"
:indeterminate="!props.selected && selected.length != 0"
></v-checkbox>
</td>
<td>{{ props.item.firstName}}</td>
<td>{{ props.item.lastName }}</td>
</template>
</v-data-table>
{{ msg }}
</v-card-text>
</v-card>
</template>
<script>
export default {
data() {
return {
headers: [
{ text: "Select", value: "id", sortable: false },
{ text: "First Name", value: "firstName", sortable: true },
{ text: "Last Name", value: "lastName", sortable: true }
],
selected: [],
members: [
{
id: 1,
firstName: "a",
lastName: "b"
},
{
id: 2,
firstName: "x",
lastName: "y"
}
]
};
},
computed: {
msg() {
const selectedRow = this.selected[0];
return selectedRow ? `${selectedRow.firstName} ${selectedRow.lastName}` : "no data selected";
}
}
};
</script>

Reload a Component

I have 3 components. They are dashboard.vue, datatable.vue and modalbody.vue. After login my application reach in dashboard.vue. Where I call datatable.vue to display a list with some props. I have a button in datatable.vue. If I click on that button a modal will open to add new record to add that list (datatable.vue) using modalbody.vue.
Now I need to reload that list (datatable.vue) after inserting new record through modal (modalbody.vue).
How can I do that ?
I am going to show you a simple example how to do it.Hope you will get the logic
Component which has the table:
<template>
<div>
<cmp :modal.sync="modal" #personAdded="addItemInTable"></cmp>
<button #click="addNewPerson">add person</button>
<table>
<tr v-for="row in tableRows">
<td>{{ row.name }}</td>
<td>{{ row.lastName }}</td>
</tr>
</table>
</div>
</template>
<script>
import childComponent from 'ChildComponent.vue'
export default {
components: {
"cmp": childComponent
},
data() {
return {
modal: false,
tableRows: [
{ name: "person1", lastName: "lperson1" },
{ name: "person2", lastName: "lperson2" },
{ name: "person3", lastName: "lperson3" },
{ name: "person4", lastName: "lperson4" },
]
}
},
methods: {
addNewPerson() {
this.modal = true //open the modal
},
addItemInTable(data) {
//saving the data passed from modal
this.tableRows.unshift(data)
this.modal = false
}
}
}
</script>
Modal Component:
<template>
<div id="modal" v-if="modal">
<input type="text" v-model="name">
<input type="text" v-model="">
<button #click="save">Save</button>
<button #click="cancel">Cancel</button>
</div>
</template>
<script>
export default {
props: {
modal: {
default: false
}
},
data() {
return {
name: '',
lastName: ''
}
},
methods: {
save() {
const savedData = {
name: this.name,
lastName: this.lastName
}
this.$emit('personAdded', savedData)
},
cancel() {
this.$emit('update:modal', false)
}
}
}
</script>
<style>
.modal {
position: absolute;
height: 500px;
width: 500px;
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
}
</style>
I dont have run the above code but the logic is correct.Please read below to understand:
For .sync modifier read this
For emitting events ($emit) read this
For reusing components read this

Vuetify Using datatable with external data from an API with Vuex

I want to use the vuetify framework with Vuex , but there is limited documentation about using it with Vuex.
I want to:
Get data from an external API ( but only the data needed )
Then Save the data in state and edit or whatever
Then push any changes back to the api
I have tried some of the external pagination and sorting examples with vuetify , but I can't get it to show all record count unless I hard code it.
I am quite new to Vue and Vuetify , so maybe I am misunderstanding something.
<template>
<div>
<v-data-table
:headers='headers'
:items='items'
:length='pages'
:search='search'
:pagination.sync='pagination'
:total-items='totalItemCount'
class='elevation-1'
>
<template slot='items' slot-scope='props'>
<td class='text-xs-right'>{{ props.item.id }}</td>
<td class='text-xs-right'>{{ props.item.first_name }}</td>
<td class='text-xs-right'>{{ props.item.last_name }}</td>
<td class='text-xs-right'>{{ props.item.avatar }}</td>
</template>
</v-data-table>
</div>
</template>
<script>
import moment from 'moment'
import axios from 'axios'
export default {
name: 'test-table',
watch: {
pagination: {
async handler () {
const rowsPerPage = this.pagination.rowsPerPage
// const skip = (this.pagination.page - 1) * rowsPerPage
const pageNumber = this.pagination.page
const res = await axios.get(`https://reqres.in/api/users?page=${pageNumber}&per_page=${rowsPerPage}`)
this.items = res.data.data
this.$store.commit('saveTableData', this.items)
},
deep: true
}
},
computed: {
pages () {
return 171
},
totalItemCount () {
return 400
}
},
async mounted () {
const rowsPerPage = this.pagination.rowsPerPage
const skip = (this.pagination.page - 1) * rowsPerPage
const res = await axios.get(`https://reqres.in/api/users?page=${skip}&per_page=${rowsPerPage}`)
this.items = res.data.data
this.$store.commit('saveTableData', this.items)
},
methods: {
nzDate: function (dt) {
return moment(dt).format('DD/MM/YYYY')
}
},
data: () => ({
search: '',
// totalItems: 0,
items: [],
pagination: {
sortBy: 'Date'
},
headers: [
{ text: 'ID', value: 'id' },
{ text: 'First Name', value: 'first_name' },
{ text: 'Last Name', value: 'last_name' },
{ text: 'Avatar', value: 'avatar' }
]
})
}
This is my working setup:
<template>
<v-data-table
:total-items="pagination.totalItems"
:pagination.sync="pagination"
:items="rows"
:headers="columns">
<template slot="headers" slot-scope="props">
<tr :active="props.selected">
<th v-for="column in props.headers">
{{ column.value }}
</th>
</tr>
</template>
<template slot="items" slot-scope="props">
<tr>
<td v-for="cell in props.item.row">
<v-edit-dialog lazy>
{{ cell.value }}
<v-text-field
:value="cell.value"
single-line
counter>
</v-text-field>
</v-edit-dialog>
</td>
</tr>
</template>
</v-data-table>
</template>
<script>
export default {
data: () => ({
pagination: {
page: 1,
rowsPerPage: 10,
totalItems: 0
},
selected: []
}),
computed: {
columns: {
get () {
return this.$store.state.columns
}
},
rows: {
get () {
return this.$store.state.rows
}
}
},
methods: {
async getRowsHandler () {
try {
const {total} = await this.$store.dispatch('getRows', {
tableIdentifier: this.$route.params.tableIdentifier,
page: this.pagination.page,
size: this.pagination.rowsPerPage
})
this.pagination.totalItems = total
} catch (error) {
// Error
}
}
}
}
</script>
I didn't implement everything. If you miss a specific part ask again and I will update my example. One more tip: You should avoid watch deep wherever possible. It can result in heavy calculations.
Assuming this is Vuetify v1.5, the documentation on the total-items prop on data-tables states:
Caution: Binding this to a blank string or using in conjunction with
search will yield unexpected behaviours
If you remove the 'search' prop from your table the record count will show again. If you're doing external stuff anyway, you'll won't want the default search functionality.