Vuetify form reset impacts parent binding - vue.js

I am facing a very weird situation with Vuetify forms. I have a Vuetify data table that follows the example used in the documentation as well as a dialog box that in fact includes a form. I am using prop to pass data from parent to child and I am following Vue and its best practice which consist of using prop as initial value for data variable. I then use to populate the form. I can edit the form and submit new values without any issue, but I created a cancel button that clears the form. when I do that, it also clears the parent fields that are in my data table. it is like the form reset is propagated from the child to its parent. so with the code hereunder, the {{props.item.order.dueDate}} is cleared when I clear the form of the child.v-bind is not supposed to behave like that.
I lost too many hours trying to find a reason but could not find anything
thanks
here is the code of the data table (parent)
<template>
<v-card>
<v-card-title class="display-3">
{{ tableTitle }}
<v-spacer></v-spacer>
<v-text-field
v-model="search"
append-icon="search"
label="Search"
single-line
hide-details
></v-text-field>
</v-card-title>
<v-data-table
:headers="headers"
:search="search"
:items="orderItems"
item-key="_id"
class="elevation-1"
:expand="expand"
>
<template v-slot:items="props">
<tr #click="props.expanded = !props.expanded">
<td class="px-2">{{ props.item.id }}</td>
<td class="text-xs px-2">{{ props.item.title }}</td>
<td class="text-xs px-2">{{ props.item.order.date | date }}</td>
<td class="text-xs px-2">{{ props.item.order.dueDate | date }}</td>
<td class="text-xs px-2">{{ props.item.count }}</td>
<td v-if="props.item.status" class="text-xs px-2">{{ props.item.status.type }}</td>
<td v-else class="text-xs px-2">New Order</td>
<td v-if="props.item.status" class="text-xs px-2"><span><v-icon color="green darken-2" v-if="props.item.status.signalSent">mail_outline</v-icon><v-icon #click.stop="sendSignal(props.item)" color="red darken-2" v-else>unsubscribe</v-icon> {{ props.item.status.datetime | date}}</span></td>
<td v-else class="text-xs px-2">-</td>
</tr>
</template>
<template v-slot:expand="props">
<v-card flat>
<list-orders-details v-bind:order="props.item"></list-orders-details>
</v-card>
<v-btn
#click.stop="showDialog()"
color="red darken-2"
fixed
dark
small
bottom
right
fab
>
<v-icon>edit</v-icon>
</v-btn>
<v-dialog v-model="editOrder" persistent>
<edit-order-item ref="clearEdit" v-bind:itemChild="props.item"></edit-order-item>
<v-btn class="cancel" dark color="red darken-2" #click="cancelEdit()">cancel</v-btn>
</v-dialog>
</template>
<v-alert slot="no-results" :value="true" color="error" icon="warning">
Your search for "{{ search }}" found no results.
</v-alert>
</v-data-table>
</v-card>
</template>
in the script of the parent:
cancelEdit() {
this.editOrder=false
this.$refs.clearEdit.clear()
}
and an extract of the child :
<template>
<v-form
#submit.prevent
ref="form"
class="white"
>
<v-container>
<h4>Status</h4>
<v-divider class="red darken-2"></v-divider>
<v-layout row wrap align-center justify-start>
<v-flex xs12 sm4 md3>
<v-select
:items="status"
v-model="orderItem.itemStatus.type"
outline
label="Status"
></v-select>
...
export default {
name: 'EditOrderItem',
components: {
},
props: ['itemChild'],
data() {
return {
dialog: false,
order: {
apikey: this.itemChild.order.apikey,
orderId: this.itemChild.order.id,
...
},
},
methods: {
submit () {
const formContent = {
order_id: this.itemChild.order._id,
item_id: this.itemChild._id,
order: this.order,
item: this.orderItem,
}
console.log(formContent)
},
clear () {
this.$refs.form.reset()
}
},

Related

v-img shows url instead of image in vuetify

I am using vuetify 2.3.10. The issue I am facing here is the v-img shows the image_url instead of the image. I tried something like this https://stackoverflow.com/a/53411531/12763413 but it didnt work for me. Please help me find the issue.
<template>
<v-container fluid grid-list-lg>
<v-card flat>
<v-data-table
:headers="headers"
:items="users"
v-model="users"
:sort-by="['full_name']"
class="elevation-1"
>
<template slot="items" slot-scope="props">
<td><v-img class="user-image" :src='props.item.image_url'/></td>
<td class="full-name">{{ props.item.full_name }}</td>
</template>
</v-data-table>
</v-card>
</v-container>
</template>
<script>
export default {
props: ['users'],
data: () => ({
headers: [
{ text: '', value: 'image_url', sortable: false },
{ text: 'Name', value: 'full_name', sortable: false }
]
})
};
</script>
Problem is not in v-img but in your usage of v-data-table - You are using non-existent slot items (should be item) so v-data-table is ignoring your slot content and rendering content of your users data as is...that's why you see url's instead of images
Also you should wrap the content item slot into <tr></tr>
<template>
<v-container fluid grid-list-lg>
<v-card flat>
<v-data-table
:headers="headers"
:items="users"
v-model="users"
:sort-by="['full_name']"
class="elevation-1"
>
<template slot="item" slot-scope="props">
<tr>
<td><v-img class="user-image" :src='props.item.image_url'/></td>
<td class="full-name">{{ props.item.full_name }}</td>
</tr>
</template>
</v-data-table>
</v-card>
</v-container>
</template>

Changing row font color of rows with vuejs and veuntify

I need to change the row font color of a vuetify v-data-table.
I receive from my backend a set of rows and each row has a column called "color_status".
I have created a template to do it. But, the problem is that when executed it shows all fields.
I´d like to show only the fields of the header.
Is there a simple way to do it?
<v-data-table
:headers="headers"
:items="fila"
:options.sync="options"
:server-items-length="total"
:loading="loading"
sort-by="nome"
:items-per-page="5"
:search="search"
>
<template v-slot:item="{ item }">
<tr :style="{ color: getColorStatus(item) }">
<td v-for="key in Object.keys(item)" :key="key">
{{ item[key] }}
</td>
</tr>
</template>
<template v-slot:item.ativo="{ item }">
<v-icon color="success">
{{
item.ativo.trim() == "T"
? "mdi-checkbox-marked"
: "mdi-checkbox-blank-outline"
}}
</v-icon>
</template>
<template v-slot:no-data>
<v-btn color="primary" #click="restart">Reiniciar</v-btn>
</template>
<template v-slot:item.nome="{ item }">
<router-link :to="{ name: 'EditFila', params: { id: item.id } }">{{
item.nome
}}</router-link>
</template>
</v-data-table>
methods:{
getColorStatus(item) {
return item.color_status;
}
}
Why don't you loop through headers instead of Objects ?
<td v-for="key in headers" :key="key">
{{ item[key] }}
</td>

How to get data from parent into child?

I'm trying to get data from a parent component to a child's component.
In the following component I'm looping trough the array 'portfolios'.
Portfolios contains a unique ID, which I want to get.
After I got the ID, I want to emit the ID to another component.
Which way could I do this?
<v-card-text v-for="(item, index) in portfolios" :key="index">
<v-card
dark
color="gradient"
elevation="4"
class="pa-2 ml-auto mr-auto justify-center"
max-width="1000px"
>
<v-list-item three-line>
<v-list-item-content color="red">
<div class="overline mb-2">
<v-chip color="white" light x-small>Depot-Nr: {{item.portfolio_id}}</v-chip>
</div>
<v-list-item-title
class="display-1 mb-1"
>{{formatPrice(item.portfolio_value)}}€</v-list-item-title>
<v-list-item-subtitle>
Einstandwert: {{formatPrice(item.investment_capital)}}€
<br />
</v-list-item-subtitle>
</v-list-item-content>
<v-list-item-avatar size="80" color="#fff">
<v-icon color="#243B55" large>mdi-bank</v-icon>
</v-list-item-avatar>
</v-list-item>
<template v-if="!item.funds.length"></template>
<template v-else>
<v-simple-table class="ml-4 mr-4" light>
<template v-slot:default>
<thead>
<tr>
<th class="text-left">ISIN</th>
<th class="text-left">Name</th>
<th class="text-left">Stückzahl</th>
<th class="text-left">Marktpreis</th>
<th class="text-left">Positionswert</th>
<th class="text-left mr-2">Kaufpreis</th>
<th class="text-left">Performance</th>
</tr>
</thead>
<tbody>
<tr v-for="(items,index) in item.funds" :key="index">
<td>{{items.isin}}</td>
<td class="font-weight-bold">{{items.fund_name}}</td>
<td>{{items.quantity}}</td>
<td>{{formatPrice(items.marketprice)}} €</td>
<td>{{formatPrice(items.positionswert)}} €</td>
<td>{{formatPrice(items.buying_price)}} €</td>
<td>{{items.performance}} %</td>
</tr>
</tbody>
</template>
</v-simple-table>
</template>
<v-list-item-action>
<v-layout row class="ml-auto">
<AddPortfolioFundComponent></AddPortfolioFundComponent> //I want to give item.portfolio_id to this component
<template v-if="!item.funds.length"></template>
<template v-else>
<SellPortfolioFundComponent></SellPortfolioFundComponent>
</template>
</v-layout>
</v-list-item-action>
</v-card>
</v-card-text>
In vue you pass data from a parent to a child component as props. When a child needs to pass data to a parent component, the child needs to emit the data and the parent captures it. Check this link: https://v2.vuejs.org/v2/guide/components.html
<v-card
...
v-for="(item, index) in portfolios"
:key="index">
<v-card-text>
<v-list-item three-line>
...
</v-list-item>
<template v-if="!item.funds.length"></template>
<template v-else>
<v-simple-table class="ml-4 mr-4" light>
...
</v-simple-table>
</template>
<v-list-item-action>
<v-layout row class="ml-auto">
<AddPortfolioFundComponent :portfolioId="portfolio_id"></AddPortfolioFundComponent>
...
</v-layout>
</v-list-item-action>
</v-card-text>
</v-card>
The child component AddPortfolioFundComponent should have a prop initialized to accept the value being passed.
<template>
<div>
{{portfolioId}}
</div>
</template>
<script>
export default {
name: "AddPortfolioFundComponent",
props: {
portfolioId: {
type: String,
default: null
}
}
};
</script>
to send data from parent to child: in parent compenent use prop ":"
<child-component
:propName="value"
/>
in child component use :
props: ["propName"]
```

Horizontal alignment in <v-data-table> from Vuetify

I have the following table in which I can't align some items such as the checkbox and the actions:
This is the table:
<v-data-table
:headers="headers"
:items="users"
hide-actions
class="elevation-1"
>
<template slot="items" slot-scope="props">
<td>{{ props.item.email }}</td>
<td class="text-xs-left">{{ props.item.empresa.descripcion}}</td>
<v-checkbox disabled v-model="props.item.isAdmin"></v-checkbox>
<td class="text-xs-left">{{ props.item.createdAt }}</td>
<td class="justify-center layout px-0">
<v-icon
small
class="mr-2"
#click="editItem(props.item)"
>
Editar
</v-icon>
<v-icon
small
left
class="mr-2"
#click="deleteItem(props.item)"
>
Eliminar
</v-icon>
</td>
</template>
</v-data-table>
I need to align the v-checkbox and the v-icon.
There is no css in the <style> section.
Give it a try wrapping the <v-layout justify-center></v-layout> with <td></td> like the Ohgodwhy comment.
It would be like:
<v-data-table
:headers="headers"
:items="users"
hide-actions
class="elevation-1"
>
<template slot="items" slot-scope="props">
<td>
<v-layout justify-center>
{{ props.item.email }}
</v-layout>
</td>
<td>
<v-layout justify-center>
{{ props.item.empresa.descripcion}}
</v-layout>
</td>
<td>
<v-layout justify-center>
<v-checkbox disabled v-model="props.item.isAdmin"></v-checkbox>
</v-layout>
</td>
<td>
<v-layout justify-center>
{{ props.item.createdAt }}
</v-layout>
</td>
<td>
<v-layout justify-center>
<v-icon
small
class="mr-2"
#click="editItem(props.item)"
>
Editar
</v-icon>
<v-icon
small
left
class="mr-2"
#click="deleteItem(props.item)"
>
Eliminar
</v-icon>
</v-layout>
</td>
</template>
</v-data-table>
For those of you taking a simple example from the Vuetify docs like I did:
<v-card>
<v-card-title id="balloon-title">Balloon Info - tracking [balloon ID]</v-card-title>
<v-data-table disable-sort dense hide-default-footer :headers="headers" :items="info" item-key="name">
</v-data-table>
</v-card>
The solution above requires you to change your entire layout. Instead, I styled the td selector like so
td {
text-align: center !important;
}
Hope this helps!
edit- make sure this style isn't in a scoped component.
Here's a simplified snippet that iterates <td> instead of specifying each prop, using only the css class text-center instead of a whole v-layout component:
<v-data-table
item-key="yourItemKey"
:items="dataSet"
:headers="headers">
<!-- item is the row itself with the column values -->
<template v-slot:item="{ item }">
<tr>
<td v-for="(val, key) in item" :key="key" class="text-center">
{{ val }}
</td>
</tr>
</template>
</v-data-table>

vuetify Datatable resets search result each time I click on sort

I cant filter my current visible result in my datatable without triggering a new api call. Each time I click on one of the columns the datatable makes a new API call to the very same page, so its not even appending any parameters to the api call like order desc etc.. So how can I filter my result without making a new api call
My code:
<v-card>
<v-card-title>
My data
<v-spacer></v-spacer>
<v-flex xs2>
<v-text-field
append-icon="search"
label="Search data"
single-line
hide-details
v-model="search"
></v-text-field>
</v-flex>
</v-card-title>
<v-data-table
:headers="headers"
:items="items"
:total-items="pagination.totalItems"
:pagination.sync="pagination"
:search="search"
item-key="combo_id"
:rows-per-page-items="[50, 50]"
hide-actions
>
<template slot="items" slot-scope="props">
<tr #click="getCombo(props.item.combo_id); props.expanded = !props.expanded">
<td class="text-xs-left">{{ props.item.data }}</td>
<td class="text-xs-left">{{ props.item.data2 }}</td>
<td class="text-xs-left">{{ props.item.data3 }}</td>
<td class="text-xs-left">{{ props.item.data4 }}</td>
<td class="text-xs-left">{{ props.item.data5 }}</td>
<td class="text-xs-left">{{ props.item.data6 }}</td>
<td class="text-xs-left">{{ props.item.data7 }}</td>
</tr>
</template>
<template slot="expand" slot-scope="props">
<v-card flat>
<v-card-text>
<v-btn color="primary" dark #click.stop="commentDialog = true">Show comments</v-btn> Tags: <strong>{{ comboItemTags }}</strong>
<v-btn color="warning" class="right" #click.stop="editDialog = true">Edit system</v-btn>
</v-card-text>
</v-card>
</template>
</v-data-table>
<div class="text-xs-center pt-2">
<v-pagination v-model="pagination.page" :length="pages" :total-visible="10"></v-pagination>
</div>
</v-card>
data () {
return {
search: '',
items: [],
pagination: {
page: 1,
rowsPerPage: 50,
totalItems: 0
},
...
...
},
watch: {
pagination: {
handler() {
this.getAllSystemsNewPage(this.pagination.page); //Fetch new data and push into items
},
deep: true
}
},
If you're fetching data from a server and watching the pagination object, it's assumed that all processing (pagination, sorting, searching) happens server-side.
See this example. Everything that happens inside the promise in getDataFromApi is a simulation of what happens on the server. You will need to pass along the relevant data from the pagination object as parameters to your server API call and return the correct items.
edit:
How you pass along the pagination data to your backend is outside of vuetify's scope and depends on what the backend looks like. But a simple example is something like this:
getDataFromApi () {
const { sortBy, descending, page, rowsPerPage } = this.pagination
const query = `page=${page}&sort_by=${sortBy}&sort_order=${descending ? 'desc' : 'asc'}&rows_per_page=${rowsPerPage}`
axios.get(`/api/endpoint?${query}`).then(...)
}