Is there a way Vue.js provides setting data array arr to the initial value, after it had being changed with methods ? I've changed the checkboxes and its values and now want to reset the data array to initial state.
<template>
<div>
<h1>Example 1</h1>
<div
v-for="(a, i) in arr"
:key="i"
:checked="a"
#click="toggleItem(i)"
class="checkbox"
>
<div class="out">{{ a }}</div>
</div>
<div class="out">{{ arr }}</div>
<div class="out">{{ newArr }}</div>
<!-- <div class="out">{{ newArr }}</div> -->
<input #click="resetState" type="button" value="Reset" />
</div>
</template>
<script>
export default {
data() {
return {
arr: [true, false, true, false, true, true, true]
};
},
methods: {
toggleItem(index) {
this.arr.splice(index, 1, !this.arr[index]);
},
resetState() {
// set the array arr to initial data after some toggleItem() changes
},
},
};
</script>
My suggestion is to save the default value somewhere else, such as:
<script>
const defaultArr = [true, false, true, false, true, true, true];
export default {
data() {
return {
arr: [...defaultArr]
};
},
methods: {
toggleItem(index) {
this.arr.splice(index, 1, !this.arr[index]);
},
resetState() {
// set the array arr to initial data after some toggleItem() changes
this.arr = [...defaultArr];
},
},
};
</script>
Note the spreading syntax [...] is neccessary since you don't want to mutate the default array.
Related
The question has three parts revolving around two files App.vue and X Array.vue :-
1.When value of input is changed, how it could be written back to the array?
2.If the value entered is empty how to remove the element from array?
3.How to show one extra input element always so that it is possible to add new values(linked with 2)?
XArray should basically be an array editor.
App.vue
<template>
<div>
<XArray v-model="myArray" />
<pre>{{ myArray }}</pre>
</div>
</template>
<script>
import XArray from './components/XArray.vue';
export default {
components: {
XArray,
},
data: () => {
return {
myArray: ['one', 'two'],
};
},
};
</script>
XArray.vue
<template>
<input
v-for="(option, index) in modelValue"
:key="index"
#input="$emit('update:modelValue', [...modelValue, `${$event.target.value}`])"
/>
</template>
<script>
export default {
props: {
modelValue: {
type: Array,
required: true,
},
};
</script>
Please take a look at following snippet:
const app = Vue.createApp({
data() {
return {
myArray: ['one', 'two'],
}
},
methods: {
addEl() {
this.myArray.push('new')
}
}
})
app.component('child', {
template: `
<div>
<input
v-for="(option, index) in modelValue"
:key="index"
#input="$emit('update:modelValue', upd(index, $event.target.value))"
:value="option"
/>
</div>
`,
props: {
modelValue: {
type: Array,
required: true,
}
},
methods: {
upd(idx, val) {
return val ? [
...this.modelValue.map((item, i) =>
i !== idx
? item
: val
),
] : this.modelValue.length > 1 ?
[ ...this.modelValue.filter((item, i) => {
if(i !== idx) return item
})] :
[ "last" ]
}
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
<child v-model="myArray"></child>
<pre>{{ myArray }}</pre>
<button #click="addEl">add</button>
</div>
I'm working with BootstrapVue. To my problem: I have a v-for in my template in which I have two buttons.
Looping over my v-for my v-if doesn't generate unique IDs and than after clicking one button each button will be triggered (from Open me! to Close me! and other way around).
How can I manage to get each button only triggers itself and doesn't affect the other?
I think I have to use my n of my v-for but I actually don't know how to bind this to a v-if..
Thanks in advance!
<template>
<div>
<div v-for="n in inputs" :key="n.id">
<b-button v-if="hide" #click="open()">Open me!</b-button>
<b-button v-if="!hide" #click="close()">Close me! </b-button>
</div>
<div>
<b-button #click="addInput">Add Input</b-button>
</div>
</div>
</template>
<script>
export default {
data() {
return {
id: null,
inputs: [{
id: 0
}],
hide: true,
};
},
methods: {
open() {
this.hide = false
},
close() {
this.hide = true
},
addInput() {
this.inputs.push({
id: this.id += 1;
})
}
}
};
</script>
Everything seems to look fine. In order to handle each button triggers,
you can maintain an object like so:
<script>
export default {
data() {
return {
inputs: [{id: 0, visible: false}],
};
},
methods: {
open(index) {
this.inputs[index].visible = false
},
close(index) {
this.inputs[index].visible = true
},
addInput() {
this.inputs.push({id: this.inputs.length, visible: false});
}
}
};
</script>
and your template should be like
<template>
<div>
<div v-for="(val, index) in inputs" :key="val.id">
<b-button v-if="val.visible" #click="open(index)">Open me!</b-button>
<b-button v-if="!val.visible" #click="close(index)">Close me! </b-button>
</div>
</div>
</template>
Edit:
You don't need to insert an id every time you create a row, instead can use the key as id. Note that the inputs is an object and not array so that even if you want to delete a row, you can just pass the index and get it removed.
I would create an array of objects. Use a boolean as property to show or hide the clicked item.
var app = new Vue({
el: '#app',
data: {
buttons: []
},
created () {
this.createButtons()
this.addPropertyToButtons()
},
methods: {
createButtons() {
// Let's just create buttons with an id
for (var i = 0; i < 10; i++) {
this.buttons.push({id: i})
}
},
addPropertyToButtons() {
// This method add a new property to buttons AFTER its generated
this.buttons.forEach(button => button.show = true)
},
toggleButton(button) {
if (button.show) {
button.show = false
} else {
button.show = true
}
// We are changing the object after it's been loaded, so we need to update ourselves
app.$forceUpdate();
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<template>
<div>
<div v-for="button in buttons" :key="button.id">
<button v-if="button.show" #click="toggleButton(button)">Open me!</button>
<button v-if="!button.show" #click="toggleButton(button)">Close me! </button>
</div>
</div>
</template>
</div>
I have code below in vue js and I wanted to update data the model by using AddCategory and it works fine, on the other hand, I wanted to post this to API Url using the post data below, but when I set v-model="posts.AddCategory" it didn't work, is there a way to do it?
<b-form-input
id="input"
type="text"
name="AddCategory"
v-model="AddCategory" //v-model="posts.AddCategory" didn't work
placeholder="categories"
required
/>
<div
class="categories"
v-for="(category, index) in categories"
:key="index"
#click="Add(index, AddCategory)"
>
<mark> {{ category.category_name }} </mark>
<script>
export default {
name: "postComponent",
components: {
Editor: PrismEditor,
},
data() {
return {
categories: [],
selectedIndex: null,
isediting: false,
AddCategory: "",
posts: {
title: null,
question: null,
code: require("../example.js").default /* eslint-enable */,
},
};
},
methods: {
Add(AddCategory, index) {
this.AddCategory = AddCategory;
this.selectedIndex = index;
this.isediting = true;
},
Your v-model=posts.AddCategory won't work as you are hitting one of the object caveats as defined in vue https://v2.vuejs.org/v2/guide/reactivity.html#For-Objects.
What you can do is first in your Add method you could to so:
Add(category,index){
this.$set(this.post,'AddCategory',category);
....
}
I'm trying to create filtering functionality which would allow me to filter cards from a collectible card game based on several criteria. I've already created 6 functions which filter based on a single criteria like card name, card cost or card rarity. These functions work fine and do their job, however, right now I can only use one of them at a time.
What I am trying to do is combine or chain these functions so that they are all taken into account before returning the final array with cards. I'm wondering if there's any easy way to do that?
Right now I have this:
<template>
<div class="cards">
<div class="cards-list">
<div class="card" v-for='card in filteredByCost' #click='specificCard(card.cardCode)'>
<div class="card-image">
<img class='responsive-image' :src='"../assets/cards/" + card.cardCode + ".png"' alt="">
</div>
</div>
</div>
</div>
</template>
<script>
import cards from '../assets/cards/set1-en_us.json'
import router from '../router'
export default {
data() {
return {
cards: cards,
search: '',
regions: ['Demacia', 'Noxus'],
cost: [7],
attack: [3, 5],
health: [4, 7],
rarity: ['Champion']
}
},
methods: {
specificCard(cardCode){
router.push({ name: 'specificCard', params: { cardCode: cardCode } })
}
},
computed: {
filteredByName(){
return this.cards.filter((card) => {
return card.name.match(this.search)
})
},
filteredByRegion(){
return this.cards.filter((card) => {
return this.regions.includes(card.region)
})
},
filteredByCost(){
return this.cards.filter((card) => {
return this.cost.includes(card.cost)
})
},
filteredByRarity(){
return this.cards.filter((card) => {
return this.rarity.includes(card.rarity)
})
},
filteredByAttack(){
return this.cards.filter((card) => {
return this.attack.includes(card.attack)
})
},
filteredByHealth(){
return this.cards.filter((card) => {
return this.health.includes(card.health)
})
},
}
}
</script>
Information
Place all your filter methods in the methods attribute on the vue instance
Create a way to enable/disable the filters
Create a computed property that looks at your #2 in this list and applies the proper filters accordingly
My rough example
new Vue({
el: "#app",
data () {
return {
filterEnabler: {
search: false,
sort: false
},
formInputs: {
searchText: ''
},
entries: [
'vue',
'react',
'angular',
'svelte'
]
}
},
computed: {
filteredEntries () {
let { entries, filterEnabler } = this
entries = entries.slice(0)
if (filterEnabler.search) entries = this.searchFilter(entries)
if (filterEnabler.sort) entries = this.sortFilter(entries)
return entries
}
},
methods: {
searchFilter (entries) {
return entries.filter(entry => entry.indexOf(this.formInputs.searchText) !== -1)
},
sortFilter (entries) {
return entries.sort()
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="filters">
<div class="search-filter">
<input type="checkbox" v-model="filterEnabler.search" /> Search Filter
<div v-if="filterEnabler.search">
<input placeholder="type here" type="text" v-model="formInputs.searchText" />
</div>
</div>
<div class="sort-filter">
<input type="checkbox" v-model="filterEnabler.sort" /> Sort Filter
</div>
</div>
<ul>
<li v-for="entry in filteredEntries" :key="entry">{{entry}}</li>
</ul>
</div>
In my example, you can see this I have 2 filters, search and sort - when one of their filterEnablers is toggeled true, it will apply all the enabled filters to the data and then return a new, separate array (very important, try not to mutate your source of truth, in my case, that's entries)
Hope this helps!
I have an addRows array which contains a group of objects.
These objects are added dynamically when using click + button.
Then they are pushed to the addRows array; after that, the user fills these objects.
Every object has many input values such as price and quantity.
I need to change the price when the quantity changes, but the problem is if the value was added before, the returned changed item is empty.
This happens because the new value was added before in the old values array.
As shown in the image third value same first so the array returned empty value of third item
I tried foreach and for also map but I'm facing the same problem.
computed: {
originalDistValue(a , b) {
return this.addRows.map(i => {
return i.dist
})
}
}, // close computed
watch: {
originalDistValue(a , b) {
let newVal = a.filter( obj => {
return b.indexOf(obj) == -1;
})
let element = this.addRows.filter( i => {
return i.dist == newVal;
})
deep: true
console.log(newVal)
for(let i in element) {
element[i].cit = 1;
}
}
}
At first create a child component for each row
Pass each row to component
Handle the single object inside the component instead of handling an array of objects in parent
See this example:
Vue.component('child', {
props: {
value: { type: Object, default: () => ({}) }
},
data: function() {
return {
selfValue: null
}
},
watch: {
value: {
handler(value) {
this.selfValue = value
},
immediate: true,
deep: true
},
selfVaue: {
handler(value) {
// you can also validate value then emit it
this.$emit('input', value)
},
deep: true
},
'selfValue.quantity'() {
this.selfValue.price = 0
}
},
template: `
<div class="d-flex justify-content-between">
<label>
Title:
<input type="text" v-model="selfValue.title" class="form-control" />
</label>
<label>
Quantity:
<select v-model="selfValue.quantity" class="form-control">
<option value="A">A</option>
<option value="B">B</option>
</select>
</label>
<label>
Price:
<input type="tel" v-model="selfValue.price" class="form-control" />
</label>
</div>
`
})
new Vue({
el: '#app',
data: {
rows: []
},
methods: {
add() {
this.rows.push({ title: '', quantity: '', price: 0 })
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<div id="app" class="container">
<h2 class="mb-3">Rows:</h2>
<div v-for="row,i in rows">
<child v-model="row"></child>
<hr class="mt-1 mb-1" />
</div>
<button #click="add" class="mb-3">
ADD
</button>
<pre>{{ rows }}</pre>
</div>