V-model same value in loop - vue.js

I have created loops for some v-select. When I enter v-model it makes my v-select the same value. How i can fix this?
<template>
<div class="create-group">
<div class="form-group">
<button class="btn btn-defualt addGroup" v-on:click="addmember(member.value)">
<h3>Crate Group</h3>
</button>
<div class="member">
<div class="row">
<div class="col-lg-2" style="padding-right: 0;">
<h3>member in grroup :</h3>
{{member}}
</div>
<div class="col-lg-10 list" v-bind:class="{'list-member' : listActive}">
<div class="input-member" v-for="list in lists" v-bind:key="list.id">
<h3>{{list.count}}.</h3>
<div class="select">
<v-select :options="options" v-model="member"></v-select>
</div>
</div>
<div class="add-member" v-on:click="add()">
<h4>+ add member</h4>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
My data:
data() {
return {
member: [],
newitem: { name: "", type: [] },
options: [],
lists: [{ count: "1" }, { count: "2" }, { count: "3" }],
};
},

You are assigning selected value to member that is the same between all selectors. You need to use different variables for different lists, like this:
<v-select :options="options" v-model="member[list.id]"></v-select>
data() {
return {
member: {}, // <- This is object now
newitem: { name: "", type: [] },
options: [],
lists: [{ count: "1" }, { count: "2" }, { count: "3" }],
};
},

Related

Vue is not update DOM when object property changes in array of objects

I have a const array of objects which set to the data property on created(), I am trying to update the object properties as the user entered the form, the Console print shows the value updated, but the DOM is changing. DOM is updated when the key value is changed too but I do not want to change the key value
Data Array:
const invoices = [
{
number: "BBRFL",
client: "Ellison Daugherty",
amount: 8800,
status: "Paid",
},
{
number: "TYUBK",
client: "Myrna Vinson",
amount: 4097,
status: "Paid",
},
{
number: "IRUBR",
client: "Mcmillan Warner",
amount: 6466,
status: "Draft",
},
];
Here is the app.
const app = new Vue({
el: "#app",
components: {
"create-invoice": CreateInvoice,
"invoice-item": InvoiceItem,
},
data() {
return {
invoices: invoices,
status: null,
};
},
created: function () {
const invoices = localStorage.getItem("invoices");
if (invoices) {
this.invoices = JSON.parse(invoices);
}
},
computed: {
count: function () {
return this.filteredInvoices.length;
},
filteredInvoices: function () {
if (this.status) {
return this.invoices.filter((invoice) => invoice.status == this.status);
}
return this.invoices;
},
},
methods: {
saveInvoice(invoice) {
this.invoices.push(invoice);
},
invoiceUpdate(invoice) {
const index = this.invoices.findIndex((i) => i.number === invoice.number);
// this.invoices.splice(index, 1, invoice);
Vue.set(this.invoices[index], "client", invoice.client);
Vue.set(this.invoices[index], "amount", invoice.amount);
Vue.set(this.invoices[index], "status", invoice.status);
},
invoiceDelete(number) {
const index = this.invoices.findIndex((i) => i.number === number);
this.invoices.splice(index, 1);
},
},
watch: {
invoices: {
deep: true,
handler: function (invoices) {
localStorage.setItem("invoices", JSON.stringify(invoices));
},
},
},
template: `
<div>
<div class="row">
<div class="col-md-4 text-left">
<h1>Invoices</h1>
<p class="text-dark">There are {{count}} Total Invoices</p>
</div>
<div class="col-md-4 text-start" style="margin-top: 10px">
<label for="status">Filter</label>
<select name="filter" id="status" class="form-control" v-model="status">
<option value="Draft">Draft</option>
<option value="Paid">Paid</option>
<option value="Pending">Pending</option>
</select>
</div>
<div class="col-md-4 text-right" style="margin-top: 10px">
<button class="btn btn-primary mt-4" id="createInvoice">
New Invoice
</button>
</div>
</div>
<div class="col-md-12">
<div class="row mt-2 mb-2">
<div
class="invoice col-md-12"
>
<invoice-item
v-for="(n, index) in filteredInvoices"
:key="n.number"
:initial-client="n.client"
:initial-number="n.number"
:initial-amount="n.amount"
:initial-status="n.status"
#update-invoice="invoiceUpdate"
#delete-invoice="invoiceDelete"
></invoice-item>
</div>
<div class="text-center" v-if="filteredInvoices.length === 0"><p>No invoice found</p></div>
</div>
</div>
<create-invoice #saveInvoice="saveInvoice" ></create-invoice>
</div>
</div>
`,
});
I had tried, this.$set, Vue.set, Direct assigning to property, assingment using splice function, but none of working. It only works with the change of value of key in for loop. Which I do not want to update.
Jsfiddle
Any Help? Thanks in advance.

How to read child component values from a parent method -- vue.js

I have a dynamic list of custom components which are input fields. From my parent vue ,when a button is clicked, how can I write a method that will loop through all the components in my list to get their values?
input-field.vue
<template>
<div>
<input
type="text"
:name="name"
:id="id"
#input="valueChanged"
v-model="val"
/>
</div>
</template>
app.vue
<div>
<ul>
<ul v-for="item in items" :key="item.id">
<my-input :name="item.name" :id="item.id" #input="onChange" />
</ul>
</ul>
<button>READ FIELDS</button>
</div>
</template>
<script>
import myInput from "./components/input-field.vue";
export default {
name: "App",
data() {
return {
items: [
{ id: "3", name: "test3" },
{ id: "4", name: "test4" },
{ id: "5", name: "test5" },
],
};
},
components: {
myInput
},
methods: {
//on button click function here
//how can I get the data from the my-input components
}
};
</script>
There is no need to read the values. Bind them like this: Playground
Read and understand what I have done, do not copy-paste. I have changed some of your code eg. imports
input-field.vue
<template>
{{name}}
<input type="text"
:name="name"
:id="id"
:value="modelValue"
#input="$emit('update:modelValue', $event.target.value)" />
</template>
<script>
export default {
props: ["modelValue", "id", "name"],
emits: ["update:modelValue"]
}
</script>
app.vue
<template>
<div v-for="item in items" :key="item.id">
<my-input :name="item.name" :id="item.id" v-model="item.value"></my-input>
</div>
<br />
<b>Values in App.vue</b>
<table border=1>
<tr v-for="item in items" :key="item.id">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.value}}</td>
</tr>
</table>
</template>
<script>
import myInput from "./input-field.vue";
export default {
name: "App",
data() {
return {
items: [
{ id: "3", name: "test3", value: "test input" },
{ id: "4", name: "test4", value: "" },
{ id: "5", name: "test5", value: "" },
],
};
},
components: {
myInput
},
};
</script>

How to remove html tags

Hi everyone I want to not show "html tags" When i have data in tables, but i want show text only like microsoft word, I'm not sure on the support, I don't know to fix it and I have tried many times, I think can't to remove all the html tags from string, I used CKeditor to input and datatable of vue output from vueX
help me please
Example Outpout
<template>
<div class="container" id="annoucements">
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header card-header-primary card-header-icon">
<div class="card-icon">
<i class="material-icons">assignment</i>
</div>
<h4 class="card-title">
{{ $t('global.table') }}
<strong>{{ $t('cruds.annoucement.title') }}</strong>
</h4>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<div class="table-overlay" v-show="loading">
<div class="table-overlay-container">
<material-spinner></material-spinner>
<span>Loading...</span>
</div>
</div>
<datatable
:columns="columns"
:data="data"
:total="total"
:query="query"
:xprops="xprops"
:HeaderSettings="false"
:pageSizeOptions="[10, 25, 50, 100]"
>
<global-search :query="query" class="pull-left" />
<span style="margin-left:10%">
<button
type="button"
class="btn btn-default"
#click="fetchIndexData"
:disabled="loading"
:class="{ disabled: loading }"
>
<i class="material-icons" :class="{ 'fa-spin': loading }">
refresh
</i>
{{ $t('global.refresh') }}
</button>
</span>
<header-settings :columns="columns" class="pull-right" />
</datatable>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
import TranslatedHeader from '#components/Datatables/TranslatedHeader'
import HeaderSettings from '#components/Datatables/HeaderSettings'
import GlobalSearch from '#components/Datatables/GlobalSearch'
import DatatableAttachments from '#components/Datatables/DatatableAttachments'
export default {
components: {
GlobalSearch,
HeaderSettings
},
data() {
return {
columns: [
{
title: 'cruds.annoucement.fields.id',
field: 'id',
thComp: TranslatedHeader,
sortable: true,
colStyle: 'width: 100px;'
},
{
title: 'cruds.annoucement.fields.annoucement',
field: 'annoucement',
thComp: TranslatedHeader,
tdComp: DatatableAttachments
},
{
title: 'cruds.annoucement.fields.name',
field: 'name',
thComp: TranslatedHeader,
sortable: true
},
{
title: 'cruds.annoucement.fields.number',
field: 'number',
thComp: TranslatedHeader,
sortable: true
},
{
title: 'cruds.annoucement.fields.short_name',
field: 'short_name',
thComp: TranslatedHeader,
sortable: true
},
{
title: 'cruds.annoucement.fields.allow_date',
field: 'allow_date',
thComp: TranslatedHeader,
sortable: true
},
{
title: 'cruds.annoucement.fields.description',
field: 'description',
thComp: TranslatedHeader,
}
],
query: { sort: 'id', order: 'desc', limit: 100, s: '' },
xprops: {
module: 'AnnoucementsIndex',
route: 'annoucements',
permission_prefix: 'annoucement_'
}
}
},
beforeDestroy() {
this.resetState()
},
computed: {
...mapGetters('AnnoucementsIndex', ['data', 'total', 'loading'])
},
watch: {
query: {
handler(query) {
this.setQuery(query)
this.fetchIndexData()
},
deep: true
}
},
methods: {
...mapActions('AnnoucementsIndex', [
'fetchIndexData',
'setQuery',
'resetState'
])
}
}
</script>
Use method below for each cell or make an copy of data that contains string without html:
removeHtmlTags(htmlString) {
const tmp = document.createElement('DIV');
tmp.innerHTML = htmlString;
return tmp.textContent || tmp.innerText || '';
}

show/hide elements inside of for loop

I have the following:
<div v-for="num in [1,2,3,4,5]" :key="num ">
<customelement0 :num ="num" />
<span #click="show = !show" > Details: </span>
<customelement1 v-if="show" :num ="num" />
<hr />
</div>
and:
export default {
data() {
return {
show: false
};
},
};
However, in this implementation Whenever show changes it affects all of the customelement1s and will show/hide all of them.
How would one solve this problem, so that whenever a user clicks on the span it only shows/hides one element in the loop?
P.S.: in reality, the length of the loop is much longer, than what's indicated above
The problem here is essentially that your show is a variable for the entire component, and isn't linked to one of your array elements.
Generally, we don't really tend to hard-code an array into the html, but rather in the data, as shown in the other answers.
The other answers show it with num being coded into the object but you could also do something like this. Note that other field is optional and not required. The main advantage of this method is that you don't need to code every number.
<div v-for="(item, i) in items" :key="i">
<!-- Note I do i+1 so that it matches the 1,2,3 in your example -->
<!-- if you're okay with having 0,1,2 you can omit the +1 -->
<customelement0 :num="i+1" />
<span #click="item.show = !item.show" >Details: </span>
<customelement1 v-if="item.show" :num="i+1" />
<hr />
</div>
export default {
data() {
return {
items: [
{show: false, other: "foo"},
{show: false, other: "bar"},
//...
],
};
},
};
You can change number to object and toggle property:
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="todo in todos">
<label>
<input type="checkbox"
v-on:change="toggle(todo)"
v-bind:checked="todo.done">
<del v-if="todo.done">
{{ todo.text }}
</del>
<span v-else>
{{ todo.text }}
</span>
</label>
</li>
</ol>
</div>
new Vue({
el: "#app",
data: {
todos: [
{ text: "1", done: false },
{ text: "2", done: false },
{ text: "3", done: true },
{ text: "4", done: true },
{ text: "5", done: true },
]
},
methods: {
toggle: function(todo){
todo.done = !todo.done
}
}
})
You can to save the value isShow for each element in the array. So you need to use an array of objects in you data and do something like this:
export default {
data() {
return {
numList: [
{
num: 1,
isShow: true
},
{
num: 2,
isShow: true
}
]
};
},
};
In your template:
<div v-for="item in numList" :key="item.num">
<customelement0 :num="item.num" />
<span #click="item.isShow = !item.isShow" > Details: </span>
<customelement1 v-if="item.isShow" :num="item.num" />
<hr />
</div>

Multiple Dynamic Checkboxes with input groups in vue js

I am trying to make a form which contains an input group section, in this group, there are one select box and multiple checkboxes. Checkboxes are populated based on the select box selection. There is also an add and remove button to generate and remove input group. The select box is used with v-model to filtered the checkboxes. But when I generate a new input group and make changes, all checkboxes are changed.
I want them to be isolated. How can I achieve?
Here is my Template.
<template>
<form #submit.prevent="onSubmit">
<div v-for="(variationProduct, index) in variationProducts" :key="variationProduct.id">
<div class="from-group mb-4">
<label class="col-form-label"><b>Categories :</b></label>
<select class="form-control mr-2" ref="categories" v-model="category">
<option value="0">Please select category...</option>
<option v-for="category in categories" :key="category.id" :value="category.id">
{{ category.name }}
</option>
</select>
<div v-if="hasError">
<validation-errors v-if="errors['categories.'+index]" :errors="errors">
{{ errors['categories.'+index][0] }}
</validation-errors>
</div>
</div>
<div class="form-group mb-4">
<label class="col-form-lablel"><b>Variation Products :</b></label>
<div class="row">
<div v-for="filteredVariationProduct in filteredVariationProducts" :key="filteredVariationProduct.id">
<div class="col-12">
<input :id="filteredVariationProduct.id" :value="filteredVariationProduct.id" type="checkbox" ref="variationProducts">
<label :for="filteredVariationProduct.id">{{ filteredVariationProduct.name }}</label>
</div>
</div>
</div>
<div v-if="hasError">
<validation-errors v-if="errors['variationProducts.'+index]" :errors="errors">
{{ errors['variationProducts.'+index][0] }}
</validation-errors>
</div>
</div>
<div class="float-right">
<button #click.prevent="add" class="btn btn-success">Add</button>
<button #click.prevent="remove(index)" class="btn btn-danger">Remove</button>
</div>
<br>
<br>
<hr>
</div>
<input type="submit" class="btn btn-primary" value="Submit">
</form>
</template>
Here is my JS.
<script>
import ValidationErrors from './ValidationErrors.vue';
export default {
components: {
'validation-errors': ValidationErrors,
},
data () {
return {
variationProducts: [],
categories: [
{ id: 1, name: 'Technology'},
{ id: 2, name: 'Business'},
{ id: 3, name: 'Marketing'},
{ id: 4, name: 'Development'},
{ id: 5, name: 'Engineering'},
],
category: 0,
activeVariationProducts: [],
count: 1,
errors: {},
hasError: false,
}
},
methods: {
add: function() {
this.count++;
this.errors = {};
this.hasError = false;
this.variationProducts.push({ id: this.count });
},
remove: function (index) {
if ( this.variationProducts.length > 0 && index > -1) {
this.variationProducts.splice(index, 1);
} else {
alert('Must have at least one input!')
}
},
onSubmit: function() {
console.log(this.$refs.variationProducts.value);
},
generateVariationProducts: function(num) {
for(let i = 1; i <= num; i++) {
let activeVariationProduct = {
id: i,
name: 'Product '+ i,
category_id: i
};
this.activeVariationProducts.push(activeVariationProduct);
}
},
},
computed : {
filteredVariationProducts: function () {
let categoryId = parseInt(this.category);
if (categoryId !== 0) {
let filteredVariationProducts = this.activeVariationProducts.filter((variationProduct) => {
return variationProduct.category_id === categoryId;
});
return filteredVariationProducts;
} else {
let filteredVariationProducts = this.activeVariationProducts;
return filteredVariationProducts;
}
}
},
created () {
this.variationProducts.push({ id: this.count });
this.generateVariationProducts(10);
},
}
</script>
Here is a sample code. This code Shows how you can use multiple Checkboxes that is generated dynamically and how to make them isolated -
new Vue({
el : "#app",
data : {
Items : ["One", "Two", "Three"],
newCheckbox : "",
SelectedItems : {
'One' : "",
'Two' : "",
'Three' : "",
},
},
methods:{
add:function(){
Vue.set(this.SelectedItems, this.newCheckbox, false);
this.Items.push(this.newCheckbox);
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.22/vue.min.js" type="text/javascript"></script>
<div id="app">
<div>
<label v-for="(item, index) in Items">
<input type="checkbox" v-bind:key="index" v-model="SelectedItems[item]"> {{ item }}
</label>
</div>
<div>
<input type="text" v-model="newCheckbox">
<button #click="add">Add</button>
</div>
<div>
Output : {{ SelectedItems }}
</div>
</div>
You can dynamically add new checkbox and still they are isolated