Below is an example of an error.
Reproduce the problem: Add 3 items and remove the second.
Everything inside is deleted correctly. This can be seen from the rendering of the text below. But the component is not displayed correctly. Why? Is this a bug?
I tried to use an additional property for conditional rendering, tried to overwrite references to an array of elements and inside the array - no results.
Vue.component('selected-material', {
props: [
'value'
],
template: `
<div>
<v-autocomplete
v-model="local"
:items="materials"
item-text="name"
return-object
autocomplete="new-password"
#change="input"
/>
</div>
`,
data() {
return {
local: this.value,
materials: [{
id: 1,
name: 'mat-1',
q: 1
},
{
id: 2,
name: 'mat-2',
q: 1
},
],
};
},
methods: {
input() {
this.$emit('input', this.local);
},
},
})
Vue.component('add-board', {
props: [
'value'
],
template: `
<div>
<v-container fluid class="my-0 py-0">
<v-row class="my-0 py-0">
<v-col class="my-0 py-0">
<selected-material
v-model="local.material"
/>
</v-col>
<v-col class="my-0 py-0">
<v-text-field
class="my-0 py-0"
v-model="local.material.q"
/>
</v-col>
<v-col class="my-0 py-0">
<v-row class="my-0 py-0">
<v-col class="my-0 py-0">
<v-btn
class="my-0 py-0"
color="success"
icon
#click="append"
>
<v-icon>mdi-plus</v-icon>
</v-btn>
</v-col>
<v-col class="my-0 py-0">
<v-btn
class="my-0 py-0"
color="error"
icon
#click="remove"
>
<v-icon>mdi-minus</v-icon>
</v-btn>
</v-col>
</v-row>
</v-col>
</v-row>
</v-container>
</div>
`,
data() {
return {
local: this.value,
};
},
methods: {
input() {
this.$emit('input', this.local);
},
append() {
this.$emit('append');
},
remove() {
this.$emit('remove', this.local.id);
},
},
})
new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
boards: [],
};
},
mounted() {
this.append();
},
methods: {
append() {
this.boards.push({
id: Date.now(),
material: {
id: 1,
name: 'mat-1',
q: 1
},
});
},
remove(id) {
if (this.boards.length !== 1) {
const index = this.boards.findIndex(board => board.id === id);
this.boards.splice(index, 1);
}
},
},
})
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#5.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<div id="app">
<v-app>
<v-main>
<v-row v-for="(board, index) in boards" :key="index">
<v-col>
<add-board :key="index" v-model="boards[index]" #append="append" #remove="remove" />
</v-col>
</v-row>
<v-row v-for="(board, index) in boards" :key="`_${index}`">
<v-col>
{{ board.id }} | {{ board.material.q }}
</v-col>
</v-row>
</v-main>
</v-app>
</div>
UPD:
After replaced with ID:
When removing items from a v-for list, it's important to use a key that's unique to each item if you don't want the DOM to be reused. If you use index and remove an item, the next item takes its index, so Vue reuses the DOM of the removed item.
Use the id as a key, since that seems to be unique:
<v-row v-for="(board, index) in boards" :key="board.id">
Also, check the v-model on the <v-text-field>, it seems like you might intend for it to use local.material.q rather than local.q:
<v-text-field
class="my-0 py-0"
v-model="local.material.q"
/>
Related
I have an expansion panel and a v-for on it. I want to keep the panel with the same id as the logged-in user on top, and the others render last. My code is like it now:
<v-expansion-panels>
<v-expansion-panel v-for="(sign, index) in demandSignatures" :key="index">
<v-expansion-panel-header
#click="dataEdit(sign)"
:hide-actions="userID !== sign.userEid"
:disabled="userID !== sign.userEid"
>
<v-row>
<v-col cols="1"></v-col>
<v-col cols="4">
{{ sign.userFullName }}
</v-col>
<v-col>
<span
v-if="sign.signatureStatus === 'Accepted'"
class="text-success"
>امضا شده</span
>
<span
v-else-if="sign.signatureStatus === 'Rejected'"
class="text-danger"
>رد شده</span
>
<span
v-else-if="sign.signatureStatus === 'pending'"
class="text-warning"
>در انتظار تایید</span
>
</v-col>
</v-row>
</v-expansion-panel-header>
<v-expansion-panel-content v-if="userID === sign.userEid">
<v-divider />
<v-card-text class="mt-3">
<v-textarea
label="توضیحات"
v-model="description"
outlined
dense
></v-textarea>
</v-card-text>
<v-card-actions class="mx-4 justify-end d-flex">
<v-btn
class="me-2"
icon
small
#click="signForm(sign.eid, 'Accepted', acceptText)"
>
<v-icon small color="teal">mdi-draw-pen</v-icon>
</v-btn>
<v-btn
class="ms-2"
icon
small
#click="signForm(sign.eid, 'Rejected', rejText)"
>
<v-icon small color="red">mdi-close</v-icon>
</v-btn>
</v-card-actions>
<v-divider />
</v-expansion-panel-content>
<v-expansion-panel-content v-else></v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
I want to keep the panel with this condition v-if="userID === sign.user-id on top
Can anyone help with it?
Modify your demandSignatures data by plucking the item whose id is equal to userID and put it in the first position. You can do this job on the mounted hook.
Here is the working demo-
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<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#2.x/dist/vuetify.min.css" rel="stylesheet">
</head>
<body>
<div id="app">
<v-app>
<v-expansion-panels>
<v-expansion-panel v-for="(sign, index) in demandSignatures" :key="sign.userEid">
<v-expansion-panel-header
#click="dataEdit(sign)"
:hide-actions="userID !== sign.userEid"
:disabled="userID !== sign.userEid"
>
<v-row>
<v-col cols="1"></v-col>
<v-col cols="4">
{{ sign.userFullName }}
</v-col>
<v-col>
<span
v-if="sign.signatureStatus === 'Accepted'"
class="text-success"
>امضا شده</span
>
<span
v-else-if="sign.signatureStatus === 'Rejected'"
class="text-danger"
>رد شده</span
>
<span
v-else-if="sign.signatureStatus === 'pending'"
class="text-warning"
>در انتظار تایید</span
>
</v-col>
</v-row>
</v-expansion-panel-header>
<v-expansion-panel-content v-if="userID === sign.userEid">
<v-divider />
<v-card-text class="mt-3">
<v-textarea
label="توضیحات"
v-model="description"
outlined
dense
></v-textarea>
</v-card-text>
<v-card-actions class="mx-4 justify-end d-flex">
<v-btn
class="me-2"
icon
small
#click="signForm(sign.eid, 'Accepted', acceptText)"
>
<v-icon small color="teal">mdi-draw-pen</v-icon>
</v-btn>
<v-btn
class="ms-2"
icon
small
#click="signForm(sign.eid, 'Rejected', rejText)"
>
<v-icon small color="red">mdi-close</v-icon>
</v-btn>
</v-card-actions>
<v-divider />
</v-expansion-panel-content>
<v-expansion-panel-content v-else></v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<script>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
userID: 3,
description: null,
demandSignatures: [{
userEid: 1,
userFullName: "I am not equal to userID",
signatureStatus: "Accepted"
},
{
userEid: 2,
userFullName: "I am not equal to userID",
signatureStatus: "pending"
},
{
userEid: 3,
userFullName: "I am equal to userID",
signatureStatus: "Rejected"
},
{
userEid: 4,
userFullName: "I am not equal to userID",
signatureStatus: "Rejected"
},
],
};
},
mounted() {
this.reOrder();
},
methods: {
reOrder() {
// Find the index of item who match with userID
let index = this.demandSignatures.findIndex(
(item) => item.userEid == this.userID,
);
// If item found
if (index != -1) {
// save it in some local variable
let item = this.demandSignatures[index];
// remove it from its current position
this.demandSignatures.splice(index, 1);
// and put it at the first position
this.demandSignatures.unshift(item);
}
},
dataEdit() {},
signForm() {},
},
})
</script>
</body>
</html>
I am showing the data in for in my project that I created with vue js. Finally, I want to add product photos in the form. I tried as below but no result. I didn't get any errors either, which I can share. Any ideas how I can fix this or do it differently?
<v-col cols="12" sm="6" md="3" class="d-flex child-flex" :items="products.items" >
<span
>Photos: </span>
<v-card slot="item" slot-scope="props"
class="mx-auto"
style="width: 200px; height: 200px; "
>
<v-img :src="props.image.url"
></v-img>
</v-card>
</v-col>
fetch data
fetch('example/products')
.then((response) => {
return response.json();
})
.then((data) => {
this.products = data;
});
These are the data from the API.
"items": [
{
"id": 60,
"brand": {
"name": "Samsung"
},
"productImages": [
{
"id": 1,
"image": {
"url": "example.com"
}
}
],
You can try with v-for loop:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
items: [{"id": 60, "brand": {"name": "Samsung"},
"productImages": [{"id": 1, "image": {"url": "https://picsum.photos/200"}},{"id": 2, "image": {"url": "https://picsum.photos/200"}}]}],
}
}
})
slot="item" slot-scope="props"
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app>
<v-main>
<v-container>
<span>Photos: </span>
<v-col cols="12" sm="6" md="3" class="d-flex child-flex" v-for="item in items[0].productImages" :key="item.id" >
<v-card class="mx-auto" style="width: 200px; height: 200px;">
<v-img :src="item.image.url"></v-img>
</v-card>
</v-col>
</v-container>
</v-main>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
I have this snippet:
<div id="app">
<v-app id="inspire">
<v-row justify="center" align="center">
<v-col v-for="col in colors" :key="col" class="shrink" style="min-width: 220px;">
<v-text-field v-model="col" hide-details class="ma-0 pa-0" solo>
<template v-slot:append>
<v-menu v-model="menu" top nudge-bottom="105" nudge-left="16" :close-on-content-click="false">
<template v-slot:activator="{ on }">
<div :style="swatchStyle" v-on="on" />
</template>
<v-card>
<v-card-text class="pa-0">
<v-color-picker v-model="col" flat />
</v-card-text>
</v-card>
</v-menu>
</template>
</v-text-field>
</v-col>
</v-row>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
color: '#1976D2',
colors: [
"#1aaa31",
"#1ccc31",
],
menu: false,
}),
computed: {
swatchStyle() {
const { color, menu } = this
return {
backgroundColor: color,
cursor: 'pointer',
height: '30px',
width: '30px',
borderRadius: menu ? '50%' : '4px',
transition: 'border-radius 200ms ease-in-out'
}
}
}
})
Here is a pen
Each of them (color-pickers) closing right after selecting, I want to prevent this, it's working fine with a single component.
I've already set :close-on-content-click to false, but don't really understand why they opening at once and closing right after click without v-for it works fine.
I am a vuejs newbie and i am having difficulties trying to make my objects return back in a row they are in a column.
components/sellingItems.vue
<template>
<v-container class="my-5">
<v-row>
<v-col
sm="6"
md="4"
>
<v-card outlined>
<v-img :src="image" height="200px" />
<v-card-title> {{ name}} </v-card-title>
<v-card-subtitle> ${{ price }}</v-card-subtitle>
<v-card-actions>
<v-btn #click="addToCart" color="success" outlined >
<v-icon small left> add </v-icon>
Add to Cart
</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
props: ['id', 'image', 'name', 'price'],
}
</script>
pages/index.vue
After adding the for loop my products list turns into a column, i prefer it in a row to suit my aim
The Index Page
Stack overflow wont let me post the index page code so heres a screenshot
Check this
use a row and columns by editing your class to determine how many items per row I have added code in codepen
https://codepen.io/Juan-Carlos-MA/pen/yLMdLRX?editors=1010
<div id="app">
<v-app id="inspire">
<br>
<br>
<v-slider
v-model="fruits"
:tick-labels="ticksLabels"
:max="3"
step="1"
ticks="always"
tick-size="4"
></v-slider>
<div class="row">
<div v-for="item in items" :class="selection">
<v-card :loading="loading" class="mx-auto my-12" max-width="374">
<template slot="progress">
<v-progress-linear color="deep-purple" height="10" indeterminate></v-progress-linear>
</template>
<v-img height="250" src="https://cdn.vuetifyjs.com/images/cards/cooking.png"></v-img>
<v-card-title>{{ item.mensaje }}</v-card-title>
<v-divider class="mx-4"></v-divider>
<v-card-title>{{ item.precio }}</v-card-title>
<v-card-actions>
<v-btn color="deep-purple lighten-2" text #click="reserve">
Add TO CART
</v-btn>
</v-card-actions>
</v-card>
</div>
</div>
</v-app>
</div>
new Vue({
el: "#app",
vuetify: new Vuetify(),
data: () => ({
loading: false,
selection: "col-6",
value: 0,
fruits: 1,
ticksLabels: ["one", "two", "tree", "four"],
items: [
{ mensaje: "Tamales", precio: "$300" },
{ mensaje: "Atole", precio: "$300" },
{ mensaje: "Taquito", precio: "$300" },
{ mensaje: "Taquito", precio: "$300" }
]
}),
methods: {
reserve() {
this.loading = true;
setTimeout(() => (this.loading = false), 2000);
}
},
watch: {
fruits: function (val) {
console.log(val);
if (val == 0) {
this.selection = "col-12";
} else {
if (val == 1) {
this.selection = "col-6";
} else {
if (val == 2) {
this.selection = "col-4";
} else {
if (val == 3) {
this.selection = "col-3";
}
}
}
}
}
}
});
Thew problem seems to be that you are creating a new row vor every item
<template>
<v-container class="my-5">
<v-row> <!-- <=== here is your row -->
<v-col sm="6" md="4">
... etc
</v-col>
</v-row>
</v-container>
</template>
you can either move the <v-row> into the parent component and wrap <SellingItems v-for...> or pass an array (products) into the component and have the v-for within that component
<SellingItems :items="products" ... >
<template>
<v-container class="my-5">
<v-row>
<v-col sm="6" md="4" v-for="item in items"> <!-- <=== move v-for here -->
... etc
</v-col>
</v-row>
</v-container>
</template>
Vuetify provides a select field component that is used for collecting user provided information from a list of options.
Here for the documentation
The component works with check boxes, so each time you check an item, it is added at the end of the list.
Now what I'd like to do is adding the same item on the list multiple times, so each time you click the item it is added at the end of the list. Is this achievable with Vuetify?
So the image is a bit misleading. If you would like to get tag input with autosuggestions, then I would rather keep it separate.
It would be possible to enhance the example below with v-text-field, but feels kinda hacky to me.
Please let me know how does this solution suite you :)
new Vue({
el: '#app',
vuetify: new Vuetify({ icons: { iconfont: 'md' } }),
data: () => ({
options: [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
{ id: 3, name: 'fizz' }],
value: [
{ id: 1, name: 'foo' },
{ id: 1, name: 'foo' }
],
}),
})
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-row>
<v-col cols="12" sm="12">
<v-chip class='mr-2' v-for='({id, name}, i) in value'>
{{ id }}: {{ name }}
<v-icon class='ml-2' small #click='value.splice(i, 1)'>close</v-icon>
</v-chip>
<v-menu>
<template v-slot:activator='{ on }'>
<v-btn icon v-on='on' class='green'>
<v-icon>add</v-icon>
</v-btn>
</template>
<v-list>
<v-list-item v-for='o in options' #click='value.push(o)'>
<v-list-item-title>{{ o.name }}</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
</v-col>
<v-col>
<pre>{{ value }}</pre>
</v-col>
</v-row>
</v-container>
</v-app>
</div>
If you have items with same name, you need to use it's distinct property (here it is "id") to tell the difference.
and use item-text & item-value as shown in the example.
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
{ id: 3, name: 'foo' },
{ id: 4, name: 'fizz' },
{ id: 5, name: 'bar' }],
value: null,
}),
})
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<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#2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app id="inspire">
<v-container fluid>
<v-row align="center">
<v-col cols="12" sm="6">
<v-select v-model="value" :items="items" item-text="name" item-value="id" attach chips label="Chips" multiple></v-select>
</v-col>
<v-col>
<pre>{{ value }}</pre>
</v-col>
</v-row>
</v-container>
</v-app>
</div>