I'm learning how to use Vue and one of the methods in my practice code isn't working, any idea why?
When clicking 'Add name' an alert should pop up, but it doesn't.
new Vue({
el: '#array',
data: {
names: ['Jo', 'Joana', 'Joanna', 'Joan']
},
methods: {
addName: function() {
alert('Adding name');
}
}
});
<script src="https://unpkg.com/vue"></script>
<div id="array">
<ul>
<li v-for="name in names" v-text="name"> {{ names }} </li>
</ul>
</div>
<input type="text">
<button v-on:click="addName">Add name</button>
Try this.
new Vue({
el: '#array',
data: {
names: ['Jo', 'Joana', 'Joanna', 'Joan'],
newName: ""
},
methods: {
addName: function() {
this.names.push(this.newName);
this.newName = ""
}
}
});
<script src="https://unpkg.com/vue"></script>
<div id="array">
<ul>
<li v-for="name in names"> {{ name }} </li>
</ul>
<input v-model="newName" type="text">
<button v-on:click="addName">Add name</button>
</div>
Related
I wrote a simple .vue file that is supposed to show a text input, that when focused displays a list of items. What I am struggling with is that I would like that when i click on an item, it sets the value of the text input to this item. However, right now with my code, the div containing the items hides before the click event triggers.
How can I do to perform the click event before the hiding ?
Here is my code :
<script>
export default {
name: 'ListCompletion',
data() {
return {
placeholder: "Gamertag",
items: ['1', '2', '3', '4'],
toggle: false
}
}
};
</script>
<template>
<div class="Container">
<input ref="input" type="text" :placeholder="placeholder" #focus="toggle = true" #blur="toggle = false" />
<div v-show="toggle" class="list-group-flush">
<button type="button" class="list-group-item list-group-item-action" v-for="item in items" #click="$refs.input.value = item">
{{ item }}
</button>
</div>
</div>
</template>
Thanks in advance for the help.
you could try #mousedown instead. It should trigger before the #blur event.
<script>
export default {
name: 'ListCompletion',
data() {
return {
placeholder: "Gamertag",
items: ['1', '2', '3', '4'],
toggle: false
}
}
};
</script>
<template>
<div class="Container">
<input ref="input" type="text" :placeholder="placeholder" #focus="toggle = true" #blur="toggle = false" />
<div v-show="toggle" class="list-group-flush">
<button type="button" class="list-group-item list-group-item-action" v-for="item in items" #mousedown="$refs.input.value = item">
{{ item }}
</button>
</div>
</div>
</template>
You can use setTimeout:
new Vue({
el: "#demo",
data() {
return {
placeholder: "Gamertag",
items: ['1', '2', '3', '4'],
toggle: false
}
},
methods: {
handleToggle() {
setTimeout(() => this.toggle = false, 500)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<div class="Container">
<input ref="input" type="text" :placeholder="placeholder"
#focus="toggle = true" #blur="handleToggle" />
<div v-show="toggle" class="list-group-flush" >
<button type="button" class="list-group-item list-group-item-action"
v-for="item in items" :key="item" #click="$refs.input.value = item">
{{ item }}
</button>
</div>
</div>
</div>
I'm making a todo app to better understand Vue and ran into a snag.
I've gone through several StackOverflow questions and the Vuejs forum, but I'm not understanding what I'm doing incorrectly.
The problem stems from the to-do-item Component Template:
<button
#click="$emit('remove-item', {{item.id}} )">
Remove
</button>
If I replace $emit with a component method that doesn't call $emit it works fine, but when I use $emit (even in a local component function) it refuses to render.
I'm not sure why this is. Here's the rest of my code:
Vue.component("todo-item", {
props:["item"],
template: `
<li>{{ item.text }}
<button
#click="$emit('remove-item', {{item.id}} )">
Remove
</button>
</li>`
})
let vm = new Vue({
el:"#app",
data: {
text: "",
todos: []
},
methods: {
getID: (function() {
let id = 0;
return function() {
return id++;
}
}()),
addTodo: function() {
this.todos.push({id: this.getID(), text: this.text});
this.text = "";
},
remove: function(remove_id) {
this.todos = this.todos.filter(({id}) => id != remove_id);
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.min.js"></script>
<div id="app">
<div id="container">
<main>
<ul>
<todo-item
v-for="todo in todos"
:item="todo"
#remove-item="remove"
>
</todo-item>
</ul>
</main>
<div id="add-field">
<input v-model="text" /> <button id="add" #click="addTodo">Add Todo</button>
</div>
</div>
</div>
The problem is that you are trying to use template syntax inside a javascript-executed attribute:
<button
#click="$emit('remove-item', {{item.id}} )">
Fix that, and it should work:
Vue.component("todo-item", {
props:["item"],
template: `
<li>{{ item.text }}
<button
#click="$emit('remove-item', item.id )">
Remove
</button>
</li>`
})
let vm = new Vue({
el:"#app",
data: {
text: "",
todos: []
},
methods: {
getID: (function() {
let id = 0;
return function() {
return id++;
}
}()),
addTodo: function() {
this.todos.push({id: this.getID(), text: this.text});
this.text = "";
},
remove: function(remove_id) {
this.todos = this.todos.filter(({id}) => id != remove_id);
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.min.js"></script>
<div id="app">
<div id="container">
<main>
<ul>
<todo-item
v-for="todo in todos"
:item="todo"
#remove-item="remove"
>
</todo-item>
</ul>
</main>
<div id="add-field">
<input v-model="text" />
<button id="add" #click="addTodo">Add Todo</button>
</div>
</div>
Hope this helps!
So I'm creating a simple To-Do List app using VueJS:
<template>
<div>
<br/>
<div id="centre">
<div id="myDIV" class="header">
<h2 style="margin:5px">My To Do List</h2>
<input type="text" id="myInput" v-model="text" v-on:keyup.enter="AddNote()" placeholder="Title...">
<span v-on:click="AddNote()" class="addBtn">Add</span>
</div>
<ul id="myUL">
<li v-on:click="ToggleClass(index)" v-for="(item, index) in array" v-bind:class="{ checked: isChecked[index] }">
{{item}}
<span class="close">×</span>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
name: "Notepad",
data() {
return {
array: [],
text: "",
isChecked: []
}
},
methods: {
AddNote: function() {
if(this.text!=="") {
this.array.push(this.text);
this.isChecked.push(false);
this.text = "";
}
},
ToggleClass(index) {
console.log(index);
this.isChecked[index]=!this.isChecked[index];
console.log(this.isChecked);
}
}
}
</script>
However when I click on an item the v-bind attribute doesn't bind the class when I click on it. Instead it binds it when I type something in the text field above.
Can anyone please help?
The isChecked array is not reactive and vue cannot detect changes.
You have to trigger it, for example via $set or splice.
Read more about it here: https://v2.vuejs.org/v2/guide/list.html#Caveats
You can change your code like this:
ToggleClass(index) {
console.log(index);
this.isChecked.splice(index, 1, !this.isChecked[index])
// or this.$set(this.isChecked, index, !this.isChecked[index])
console.log(this.isChecked);
}
I'm sure this one's gonna be extremely easy for you guys. I am trying to make a simple list of posts with the post titles always visible, and when you click a specific post in the list, you get the post's body. I used v-show for that. However, when I click a specific post, the bodies of all of the posts appear, instead of just the one that I clicked.
Here's the template:
<template>
<div class="container">
<h1>My Posts</h1>
<ul class="list-group">
<li class="list-group-item" v-for="post in list">
<div #click="changeShow">
<h4>{{ post.title }}</h4>
<p v-show="show">{{ post.body }}</p>
<span v-show="show" class="label label-primary">ID: {{ post.userId }}</span>
</div>
</li>
</ul>
</div>
And the logic:
<script>
export default{
data(){
return{
msg:'hello vue',
list: [],
show: false
}
},
ready(){
this.fetchPostList();
},
methods:{
fetchPostList: function () {
var root = 'http://jsonplaceholder.typicode.com';
this.$http.get(root + '/posts').then(function (response) {
this.list = response.data;
})
},
changeShow: function () {
this.show = !this.show;
}
}
}
There's a few ways to approach this depending on your needs.
Multiple Open
You can make each post it's own component, that way you can have show be tied to each individual post instead of all of them.
Vue.component('post', {
template: '#post-template',
props: {
post: Object,
},
data() {
return {
show: false,
}
},
methods: {
toggleShow() {
this.show = !this.show
},
},
})
Then you can have use it like this:
<post v-for="post in posts" :post="post"></post>
One Open
If you just want one open you can pass an id as a prop and show it based on that.
Vue.component('post', {
template: '#post-template',
props: {
post: Object,
selectedId: Number,
},
computed: {
show() {
return this.post.id === this.selectedId
},
},
})
Then you can do like
<post :selected-id="selectedId" :post="post" #click="selectedId = post.id"></post>
I cheated in a different way. Just added a show property to each post and toggled that.
new Vue({
el: 'body',
data: {
list: []
},
ready: function() {
this.fetchPostList()
},
methods: {
fetchPostList: function() {
setTimeout(function() {
this.list.push({
title: 'First Post',
body: 'This is the first Post',
userId: 'Joe',
show: false
});
this.list.push({
title: 'Second Post',
body: 'This is the second Post',
userId: 'Joe',
show: false
});
this.list.push({
title: 'Third Post',
body: 'This is the third Post',
userId: 'Joe',
show: false
});
}.bind(this), 2000);
},
changeShow: function(idx) {
this.list[idx].show = !this.list[idx].show;
}
}
});
<link href="//cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css" rel="stylesheet" />
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div class="container">
<h1>My Posts</h1>
<ul class="list-group">
<li class="list-group-item" v-for="post in list">
<div #click="changeShow($index)">
<h4>{{ post.title }}</h4>
<p v-show="post.show">{{ post.body }}</p>
<span v-show="post.show" class="label label-primary">ID: {{ post.userId }}</span>
</div>
</li>
</ul>
</div>
First of all, the method that I will give an example is not useful, but I am writing to show that there is such a way.
for example
<script>
export default {
data: {
isShow: false,
postUserId: null,
},
};
</script>
<template>
<div class="container">
<h1>My Posts</h1>
<ul class="list-group">
<li class="list-group-item" v-for="post in list">
<div #click="postUserId = post.userId; isShow = !isShow">
<h4>{{ post.title }}</h4>
<p v-show="show">{{ post.body }}</p>
<span v-show="isShow && postUserId == post.userId" class="label label-primary">ID: {{ post.userId }}</span>
</div>
</li>
</ul>
</div>
</template>
I'm trying to pass data into inserted (transcluded) template.
In Angular, this would be done by modifying scope passed into transcludeFn, but I can't figure out how to do it in Vue since it simplifies things (I LOVE IT) and takes care of transclusion internally.
I've tried using v-with directive on <content> but it seems like it doesn't work for it.
Simplified component source
<template>
<div>
<ul>
<li v-repeat="tab: tabs">
<content></content>
</li>
</ul>
</div>
</template>
<script>
module.exports = {
data: function () {
return {
tabs: [{ name: 'First' }, { name: 'Second' }]
};
}
};
</script>
Usage
<div v-component="my-component">
<pre>{{ $data | json }}</pre>
</div>
Expected output
<div>
<ul>
<li>
<pre>{ "name": "First" }</pre>
<pre>{ "name": "Second" }</pre>
</li>
</ul>
</div>
Actual output
<div>
<ul>
<li>
<pre>{"tabs": [{ "name": "First" }, { "name": "Second" }]}</pre>
<pre>{"tabs": [{ "name": "First" }, { "name": "Second" }]}</pre>
</li>
</ul>
</div>
This will not work because transcluded content compiles in the parent scope.
Also now you are using v0.11.x version of Vue. Try to use v0.12.
You can pass data to the component this way:
new Vue({
el: "#app",
data: {
tabs: [{ name: 'First' }, { name: 'Second' }]
},
components: {
'my-comp': {
template: "#mycomp",
props: ['tabs'],
}
}
})
<template id="mycomp">
<div>
<ul>
<li v-repeat="tab: tabs">
<pre>{{tab | json }}</pre>
</li>
</ul>
</div>
</template>
<div id="app">
<my-comp tabs="{{tabs}}" />
</div>
<script src="https://rawgit.com/yyx990803/vue/dev/dist/vue.js"></script>