Vue :click is being triggered automatically inside v-for - vue.js

I have v-for looping and elements inside it have :click calling a function. That function is being triggered automatically on every single element on page load!
<li v-if="itemSaved.length > 0" v-for="(item, index) in items" class="list-group-item">
<div class="pull-right m-l">
<i v-if="itemSaved[index] == 'not saved'" class="icon-plus" :click="addItem(item.id)"></i>
<i v-else class="icon-check" :click="removeItem(item.id)"></i>
</div>
</li>
export default {
data: () => ({
itemSaved: [{"id":1, "name":"Jim"},{"id":2, "name":"Max"}]
}),
methods: {
addItem: function(id){
console.log('add item ' + id)
},
removeItem: function(id){
console.log('remove item ' + id)
}
}
}
Basically I get a list of add item 1 and add item 2 in the console log

Should be:
#click="removeItem(item.id)"
Not:
:click="removeItem(item.id)"
When you have :click="removeItem(item.id)" you are actually binding the result of the removeItem(item.id) method call to the click property. Which is why the method executes "automatically".
#click="handler" is a shorthand to v-on:click="handler" - the event handling directive.
:click="handler" is a shorthand to v-bind:click="handler", the property binding directive.

Related

Changing one Vuejs v-model value is changing all other values

I have a Nuxtjs/Vuejs application within which I am creating multiple Nodes. These Nodes have the Radio button for which I have assigned v-model. However, when I change the value of one Vuejs v-model is affecting all other Node Values. Following is the code sample that I have created for the Node. The ID value is unique for each Node.
<template>
<div ref="el">
<div class="header">
Node: {{ ID }}
</div>
<div>
Syntax:
<input
id="identifierTypeURN"
v-model="identifierSyntax"
type="radio"
value="URN"
name="instanceIdentifierURN"
#change="instanceIdentifiersSyntaxChange('URN')"
>
<label for="identifierTypeURN">URN</label>
<input
id="identifierTypeWebURI"
v-model="identifierSyntax"
type="radio"
value="WebURI"
name="instanceIdentifierWebURI"
#change="instanceIdentifiersSyntaxChange('WebURI')"
>
<label for="identifierTypeWebURI">WebURI</label>
</div>
</div>
</template>
I am aware that this is happening because I am using the same v-model name for all the Nodes so I changed to something like this. But still the issue persists:
<template>
<div ref="el">
<div class="header">
Identifiers
Node: {{ ID }}
</div>
<div>
Syntax:
<div v-for="node in allNodeInfo" :key="node.identifiersId">
<div v-if="node.identifiersId === ID">
<input
id="identifierTypeURN"
v-model="node.identifierSyntax"
type="radio"
value="URN"
name="instanceIdentifierURN"
#change="instanceIdentifiersSyntaxChange('URN')"
>
<label for="identifierTypeURN">URN</label>
<input
id="identifierTypeWebURI"
v-model="node.identifierSyntax"
type="radio"
value="WebURI"
name="instanceIdentifierWebURI"
#change="instanceIdentifiersSyntaxChange('WebURI')"
>
<label for="identifierTypeWebURI">WebURI</label>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
ID: '',
nodeId: '',
eventCount: '',
bizStep: '',
allNodeInfo: [],
instanceIdentifierSyntax: ''
}
},
mounted () {
this.$nextTick(() => {
const id = this.$el.parentElement.parentElement.id
const data = this.$df.getNodeFromId(id.slice(5))
this.ID = data.data.ID
this.nodeId = data.data.nodeId
this.allNodeInfo = JSON.parse(JSON.stringify(this.$store.state.modules.ConfigureIdentifiersInfoStore.identifiersArray, null, 4))
const identifiersNode = this.allNodeInfo.find(node => node.identifiersId === this.nodeId)
this.instanceIdentifierSyntax = identifiersNode.identifierSyntax
console.log(JSON.stringify(this.allNodeInfo, null, 4))
})
},
methods: {
// On change of the IdentifierSyntax change, change the value in the respective node info
instanceIdentifiersSyntaxChange (syntaxValue) {
// Change the value of the respective syntax within the Node information in IdentifiersNode array
console.log(this.ID + " --- " + syntaxValue)
}
}
}
</script>
<style>
</style>
I know I am making some small mistake where I need to differentiate each Nodes V-model but nothing is clicking me. Can someone please help me.
You have to pass your node and your v-model also to your methods within this event.. like this, because for every loop in your v-for there will be a "new created node" which includes your v-model and than you can refer to this:
#change="instanceIdentifiersSyntaxChange('URN', node, identifierSyntax)"
In your methods you just do everything based on your node.identifierSyntax = HERE WHAT YOU WANT..
Hopefully I understood the question correct and helped you out!
EDIT: (Standard procedure)
Normally it looks like this when you add a single "input" to your v-for.
Template:
<div v-for="node in inputs" :key="node.id">
<input v-model="node.someDefinition" :value="node.someDefinition"/>
<div #change="another_Function(node, someDefinition)></div>
</div>
<button #click="add_new_Input>ADD</button>
Script:
data() {
return {
id: 0,
inputs: [{ //this is representing the first input when side will be loaded
id: this.id,
//your stuff in here as well
}]
}
}
methods: {
add_new_Input() {
this.inputs.push({
id: this.id += 1
})
}
}
This should be enough.. So in your template you have a v-for where your are looping over an array (or something else but in my case it's an array) with all inputs - be aware every input of me gets an unique ID when it will be created or added.
Also you have - in my case here - an input-tag where you can get the v-model or set the :value. It's binded to my unique node which I have created in my methods.
If you pass your #change you also have to pass the current node and the v-model / value that the correct one will be changed.
Hopefully it now helps you out!

VueJs get value input radio in request axios

Hello i have this object
values: {
false: 'order expected',
true: 'order cancel'
}
I'm loop this object inside radio button i wan't to get the value (true or false) in radio button to add in my request axios i'm do that
<div
v-for="(index, type) in values"
:key="type.id"
class="filters__filter-items__type"
>
<radio-button
v-model="order"
checked-color="black"
class="filters__filter-items__type__radio"
:value="index"
:name="type"
:label="type"
/>
</div>
<button #click="applyFilters"> send </button>
export default {
data() {
return {
order:''
}
},
methods: {
async applyFilters() {
const app = { $axios: this.$axios };
const results = await endPoint.searchOrder(
app,
this.order
);
return results;
},
}
}
when i do that my request params is &params=order expected not &params=false
I don't understand why please help me thanks
When iterating an object with v-for, the first argument is the value, and the second is the key:
<div v-for="(index, type) in values">
👆value 👆key
You're currently binding the object value (i.e., order canceled, etc.) to the radio button's value, but the binding should actually use the object key (i.e., false).
A quick fix is to swap the index variable for type in the v-for:
<!-- BEFORE -->
<div v-for="(index, type) in values">
<!-- AFTER -->
<div v-for="(type, index) in values">
...or in your bindings:
<!-- BEFORE -->
<radio-button v-model="order"
:value="index"
:name="type"
:label="type"
/>
<!-- AFTER -->
<radio-button v-model="order"
:value="type"
:name="index"
:label="index"
/>
demo

accordion only show one item at same time in vue js

//this code in vue js for accordion this is working but not show open only //one at the same time.
//there component and pass props data.
<packing-material-category v-for="(category, index) in packingMaterialCategories" :category="category" :key="index"></packing-material-category>
//there template to render data.
<template>
<div class="packing-categories">
<div :class="packingCategoryClass">
<h3 class="packing-category__title" #click="toggleAccordion(category.title)" v-text="category.title" />
<div v-show="accordionOpen===true" class="packing-category__content">
<div v-if="category.description" class="packing-category__description" v-text="category.description" />
</div>
</div>
</div>
//call method for open and close
toggleAccordion() {
this.accordionOpen = !this.accordionOpen;
}
You need to track which item is shown outside of packing-material-category component (value of openedIndex) and to pass that value down to components. When value changes in the packing-material-category component, you emit event change-accordion, and in your parent component you update value of openedIndex
Try this
In your parent component, add openedIndex: 0 to data.
If you want everything closed by default, set value to false.
Update template to pass down the props index and openedIndex, so that components know when to show/hide.
<packing-material-category
v-for="(category, index) in packingMaterialCategories"
:key="index"
:index="index"
:openedIndex="openedIndex"
:category="category"
#change-accordion="(newOpen) => openedIndex = newOpen"
>
</packing-material-category>
And the packing-material-category component could look like this
<template>
<div class="packing-categories">
<div :class="packingCategoryClass">
<h3 class="packing-category__title" #click="toggleAccordion" v-text="category.title" />
<div v-show="index === openedIndex" class="packing-category__content">
<div v-if="category.description" class="packing-category__description" v-text="category.description" />
</div>
</div>
</div>
</template>
<script>
export default {
props: [
'category',
'index', // index of this component
'openedIndex', // which item should be shown/opened
],
data() {
return {
packingCategoryClass: '',
}
},
methods: {
toggleAccordion() {
// Show current item. If already opened, hide current item
let value = this.index === this.openedIndex ? false : this.index;
// notify parent component about the change
this.$emit('change-accordion', value)
}
}
}
</script>

Conditional link behavior in VueJS

Couldn't find a proper name for the title, will be glad if someone figures out a better name.
I have a component which represents a product card. The whole component is wrapped in <router-link> which leads to product page.
However I have another case, when I do not need the component to lead to a product page, but instead I need to do some other action.
The only solution I found is to pass a callback function as a prop, and based on this, do something like:
<router-link v-if="!onClickCallback">
... here goes the whole component template ...
</router-link>
<div v-if="onClickCallback" #click="onClickCallback">
... here again goes the whole component template ...
</div>
How can I do this without copy-pasting the whole component? I tried to do this (real code sample):
<router-link class="clothing-item-card-preview"
:class="classes"
:style="previewStyle"
:to="{ name: 'clothingItem', params: { id: this.clothingItem.id }}"
v-on="{ click: onClick ? onClick : null }">
However I got this: Invalid handler for event "click": got null
Plus not sure if it's possible to pass prevent modificator for click and this just looks weird, there should be a better architectural solution
Commenting on the error, you could use an empty function instead of null, in the real code snippet
<router-link class="clothing-item-card-preview"
:class="classes"
:style="previewStyle"
:to="{ name: 'clothingItem', params: { id: this.clothingItem.id }}"
v-on="{ click: onClick ? onClick : null }">
This should works (replace a for "router-link" then insert right properties)
Further infos :
https://fr.vuejs.org/v2/guide/components-dynamic-async.html
v-bind is simply an Object where each keys is a props for your component, so here, I programmatically defined an object of properties depending on the wrapper (router link or a simple div). However we cannot do this for events (of course we could create our own event listener but it's a little bit tricky) so I simply but an handle method.
new Vue({
el: "#app",
data: {
products : [{onClickCallback : () => { alert("callback"); return true;}}, {}, {}]
},
methods : {
handleClick(product, event) {
if (!product.onClickCallback) return false
product.onClickCallback()
return true
},
getMyComponentName(product) {
if (product.onClickCallback) return "div"
return "a"
},
getMyComponentProperties(product) {
if (product.onClickCallback) return {is : "div"}
return {
is : "a",
href: "!#"
}
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<component
v-for="(product, index) in products"
:key="index"
v-bind="getMyComponentProperties(product)"
#click="handleClick(product, $event)"
>
<div class="product-card">
<div class="product-card-content">
<span v-show="product.onClickCallback">I'm a callback</span>
<span v-show="!product.onClickCallback">I'm a router link</span>
</div>
</div>
</component>
</div>
Do you have to use a <router-link>? If it can safely be a <div>, you could use something like
<div #click="handleClick" ...>
<!-- component template -->
</div>
and
methods: {
handleClick (event) {
if (this.onClickCallback) {
this.onClickCallback(event)
} else {
this.$router.push({ name: 'clothingItem', ... })
}
}
}
See https://router.vuejs.org/guide/essentials/navigation.html

Vuejs v-for set unique data

I have a component that I can add when I click on the button.
<button type="submit" #click="components ++">add select box</button>
<div v-for="component in components">
<select class="selectpicker form-control" v-model="customized_exercise.name" value={{exercise.id}}>
<option v-for="exercise in exercises">{{ exercise.name }}</option>
</select>
</div>
In this template when I add a couple of them and select a value,all the other components (select box) update with the same value.How can I make them have a unique value?
Vue
import ExerciseSelectbox from './ExerciseSelectbox.vue'
export default {
methods: {
fetchexercises: function(){
this.$http.get('/api/exerciseinstructions').then(function (data) {
this.$set('exercises',data['data'])
})
},
},
comoponents: { ExerciseSelectbox },
data() {
return{
components: 1,
newCustomizedExercise : {
id:'',
name:'',
reps:'',
sets_duration:'',
weight:'',
},
numbers:[100]
}
},
ready() {
this.fetchexercises()
}
}
This is old, but I was also looking for this answer. I found that the Vue documentation mentions this:
Inside v-for blocks we have full access to parent scope properties.
v-for also supports an optional second argument for the index of the
current item.
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
So you can use this index parameter to set the id of each element in your loop to keep each one separate.
Or you can set the set your model to a name in the array. Something like v-model="customized_exercise[index].name". But I haven't actually tried this option.
I found another SO answer about how to use v-bind if you want to string concatenate the index variable in a data attribute.
<a v-bind:href="'uniqueName' + index">12312</a>