Use instance method in click handler - javascript

I have a Todo class that has a delete method. I would like to use the delete method as an #click handler:
<div v-for="todo in todos">
<v-btn #click="todo.delete">Delete</v-btn>
</div>
Unfortunately this gives me:
Invalid handler for event "click": got undefined

Here's a simple codepen example that shows the core concept working: https://codepen.io/nickforddesign/pen/YYwgKx
The issue is that the items in your todos array don't have the method.
<div class="app">
<ul>
<li v-for="todo in todos">
{{ todo }}
<button #click="todo.delete">Delete</button>
</li>
</ul>
</div>
And the js
new Vue({
el: '.app',
data() {
return {
todos: [{
name: '1',
delete() {
alert(`delete`)
}
},{
name: '2',
delete() {
alert(`delete`)
}
}]
}
}
})

Related

Property 'myfunc' was accessed during render but is not defined on instance

I don't know why but this error Property 'myfunc' was accessed during render but is not defined on instance is keeps on showing.
I am sharing my Html and js code.
const ListRenderingApp = {
data() {
return {
todos: [
{ text: 'Learn JavaScript' },
{ text: 'Learn Vue' },
{ text: 'Build something awesome' }
]
}
},
method : {
myfunc(){
console.log('drats')
}
}
}
Vue.createApp(ListRenderingApp).mount('#list-rendering')
And the Html files code is
<div id="list-rendering" class="demo">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
<button v-on:click="myfunc"> Click me</button>
</div>
I am very new to Vue.js. So, if anyone can please help me with this question.
I understood the error. And came up with the solution as well.
Firstly, In the ListRenderingApp after the data function. It should be methods. And secondly, in the console.log() statement it should be console.log(this.todos).
Working Demo :
new Vue({
el: '#app',
data: {
todos: [
{ text: 'Learn JavaScript' },
{ text: 'Learn Vue' },
{ text: 'Build something awesome' }
]
},
methods : {
myfunc() {
console.log('drats')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
<button v-on:click="myfunc"> Click me</button>
</div>

How to make it so that "Show more" & "Show less" buttons only work for the text in their own div and not for all texts using Vue.js [duplicate]

I'm making a list of items with v-for loop. Inside each item of the loop there is button with click event method that showing description text.
When i click on the button, it should toggle only inside it's own item, but it affecting all elements in v-for list.
So, how to make a toggle method that will affect only it's own item?
<template>
<div>
<div v-for="item in items" :class="{ activeclass: isActive }">
<div class="item-text">
{{item.text}}
</div>
<button #click="toggle()">show</button>
<div v-show="isActive" class="item-desc">
{{item.desc}}
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
items: [
{
text: 'Foo',
desc: 'The Array.from() method creates a new Array instance from an array-like or iterable object.',
},
{
text: 'Bar',
desc: 'The Array.from() method creates a new Array instance from an array-like or iterable object.',
}
],
isActive: false
}
},
methods: {
toggle: function () {
this.isActive = !this.isActive;
}
},
}
</script>
As #Nora said you can (and probably should) create a separate component for each list item, so you would have a component that accepts an item as a prop, then each component can have it's own isActive flag, which keeps the markup nice and clean:
Component:
Vue.component('toggle-list-item', {
template: '#list-item',
props: ['item'],
methods: {
toggle() {
this.isActive = !this.isActive;
}
},
data() {
return {
isActive: false
}
},
})
Markup
Now you can simply place the component inside your v-for:
<div id="app">
<div v-for="item in items">
<toggle-list-item :item="item"></toggle-list-item>
</div>
</div>
Here's the JSFiddle: https://jsfiddle.net/w10qx0dv/
You can add a property on each item in your list if description should be shown:
<template>
<ul>
<li v-for="item in items" :class="{ activeclass: item.isActive }">
<div class="item-text">
{{ item.text }}
</div>
<button #click="toggle(item)">show</button>
<div v-show="item.isActive" class="item-desc">
{{ item.desc }}
</div>
</li>
</ul>
</template>
<script>
export default {
data () {
return {
items: [
{
isActive: false,
text: 'Foo',
desc: 'The Array.from() method creates a new Array instance from an array-like or iterable object.',
},
{
isActive: false,
text: 'Bar',
desc: 'The Array.from() method creates a new Array instance from an array-like or iterable object.',
}
],
}
},
methods: {
toggle: function (item) {
item.isActive = !item.isActive;
}
},
}
</script>
Alternatively, you can extract the li into a separate component.
I've ran into the same problem, I solved it this way.
I took an array and pushed the clicked item's index and based on index available in the array, we can now show or hide the element.
<template>
<div>
<div v-for="(item,i) in items">
<div class="item-text">
{{ item.text }}
</div>
<button #click="toggle(i)">show</button>
<div v-show="clickedItems.includes(i)" class="item-desc">
{{ item.desc }}
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
items: [
{
text: 'Foo',
desc: 'The Array.from() method creates a new Array instance from an array-like or iterable object.',
},
{
text: 'Bar',
desc: 'The Array.from() method creates a new Array instance from an array-like or iterable object.',
}
],
clickedItems:[]
}
},
methods: {
toggle() {
if (this.clickedItems.includes(i)) {
this.clickedItems.splice(this.clickedItems.indexOf(i), 1);
} else {
this.clickedItems.push(i);
}
}
},
}
</script>

Getting index of a data in an array in VUE js

I want to change the status of Tasks when a particular method is called. But The problem is I cannot get the index of the particular item of the array to change its status.
This is my HTML:
<div class="main" id="my-vue-app">
<ul>
<li v-for="task in completeTask">
{{ task.description }} <button #click="markIncomplete">Mark as Incomplete</button>
</li>
</ul>
<ul>
<li v-for="task in incompleteTask">
{{ task.description }} <button #click="markComplete">Mark as Complete</button>
</li>
</ul>
</div>
And this is my Vue:
<script>
new Vue(
{
el: '#my-vue-app',
data:
{
tasks: [
{description:'go to market', status: true},
{description:'buy book', status: true},
{description:'eat biriani', status: true},
{description:'walk half kilo', status: false},
{description:'eat icecream', status: false},
{description:'return to home', status: false}
]
},
computed:
{
incompleteTask()
{
return this.tasks.filter(task => ! task.status);
},
completeTask()
{
return this.tasks.filter(task => task.status);
}
},
methods:
{
markComplete()
{
return this.task.status = true;
},
markIncomplete()
{
return this.task.status = false;
}
}
}
)
</script>
I need make use of markComplete() and markIncomplete() but the problem is I couldn't find the way to get the index of current element to change its status.
You could get the index by declaring a second argument at the v-for:
<li v-for="(task, index) in incompleteTask">
{{ task.description }} <button #click="markComplete(index)">Mark as Complete</button>
</li>
methods:
{
markComplete(index)
{
return this.tasks[index].status = true;
},
But a, maybe simpler, alternative is to simply **pass the `task` as argument**:
<li v-for="task in incompleteTask">
{{ task.description }} <button #click="markComplete(task)">Mark as Complete</button>
</li>
methods:
{
markComplete(task)
{
return task.status = true;
},
RTFM:
You can use the v-repeat directive to repeat a template element
based on an Array of objects on the ViewModel. For every object in the
Array, the directive will create a child Vue instance using that
object as its $data object. These child instances inherit all data
on the parent, so in the repeated element you have access to
properties on both the repeated instance and the parent instance. In
addition, you get access to the $index property, which will be the
corresponding Array index of the rendered instance.
var demo = new Vue({
el: '#demo',
data: {
parentMsg: 'Hello',
items: [
{ childMsg: 'Foo' },
{ childMsg: 'Bar' }
]
}
})
<script src="https://unpkg.com/vue#0.12.16/dist/vue.min.js"></script>
<ul id="demo">
<li v-repeat="items" class="item-{{$index}}">
{{$index}} - {{parentMsg}} {{childMsg}}
</li>
</ul>
Source:
https://012.vuejs.org/guide/list.html
Note: The directive v-repeat is available in old versions of Vue.js :-)

how to see changes in new data generated in a vue component?

Well I hope to explain, I'm generating this data from a component, when I click the checkbox changes in the generated data are not reflected, but when clicking the button with a data already in the instance changes are made, I appreciate if you explain Why or do they have a solution?
this my code
js
Vue.component('fighters', {
template: '#fighters-template',
data() {
return {
items: [
{ name: 'Ryu' },
{ name: 'Ken' },
{ name: 'Akuma' }
],
msg: 'hi'
}
},
methods: {
newData() {
this.items.forEach(item => {
item.id = Math.random().toString(36).substr(2, 9);
item.game = 'Street Figther';
item.show = false;
});
this.items.push()
},
greeting() {
this.msg = 'hola'
}
}
});
new Vue({
el: '#app'
})
html
<main id="app">
<fighters></fighters>
</main>
<template id="fighters-template">
<div class="o-container--sm u-my1">
<ul>
<li v-for="item in items">
<input type="checkbox" v-model="item.show">
{{ item.name }}</li>
</ul>
<button class="c-button c-button--primary" #click="newData()">New Data</button>
<h2>{{ msg }}</h2>
<button class="c-button c-button--primary" #click="greeting()">Greeting</button>
<hr>
<pre>{{ items }}</pre>
</div>
</template>
this live code
https://jsfiddle.net/cqx12a00/1/
Thanks for you help
You don't declare the show variables that your checkboxes are bound to, so they are not reactive – Vue is not aware when one is updated.
It should be initialized like so:
items: [
{ name: 'Ryu', show: false },
{ name: 'Ken', show: false },
{ name: 'Akuma', show: false }
]
You might think that newData would fix it, since it assigns a show member to each item, but Vue cannot detect added properties so they're still not reactive. If you initialized the data as I show above, then the assignment would be reactive.
If you want to add a new reactive property to an object, you should use Vue.set.

Updating a value when v-model changes

I have a text input with v-model binding it's value to data. I would also like to be able to call my parse() function when this v-model value changes in order to update an array that is also on data.
<div id="app">
<input
id="user-input"
type="text"
v-model="userInput">
<ul id="parsed-list">
<li v-for="item in parsedInput">
{{ item }}
</li>
</ul>
</div>
new Vue({
el: '#app',
data: {
userInput: '',
parsedInput: []
}
})
let parse = input => {
return input.split(',')
}
How should I go about updating data.parsedInput with the parse() function using the v-model change? What is the proper Vue way of doing this?
A proper Vue way of a data property that depends on another is with a computed property, that way parsedInput is automatically updated whenever userInput changes:
let parse = input => {
return input.split(',')
}
new Vue({
el: '#app',
data: {
userInput: '',
},
computed: {
parsedInput() {
return parse(this.userInput)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
<body>
<div id="app">
<input id="user-input" type="text" v-model="userInput">
<ul id="parsed-list">
<li v-for="item in parsedInput">
{{ item }}
</li>
</ul>
</div>
</body>
As a sidenote: declare the parse function before using it, to prevent is not defined errors.

Categories