I am doing a to-do list in Vuejs. When I enter a new value in the input field, it seems that I can't make the input value to reset. How can I accomplish this?
I have tried to grab the input value & reset it to an empty string, but I haven't had any luck.
HTML Code:
<div id="app">
<h1>{{ message }}</h1>
<form v-on:submit="addNewTodo">
<input class="input-value" v-model="todo.task" type="text">
<button type="submit">Add todo</button>
</form>
<ul>
<li v-for="todo in todos" :class="{ completed: todo.isActive }" #click="$set(todo, 'isActive', !todo.isActive)">
{{ todo.task }} <span v-on:click="deleteTodo">{{ todo.delete }}</span>
</li>
</ul>
</div>
JS Code:
var app = new Vue({
el: '#app',
data: {
message: 'List of things to do today',
todos: [
{ task: 'Have breakfast', delete:'(x)'},
{ task: 'Go to the gym', delete:'(x)'},
{ task: 'Study Vuejs', delete:'(x)'}
],
todo: {task: '', delete: '(x)'}
},
methods: {
addNewTodo: function(e){
e.preventDefault();
this.todos.push( this.todo );
var inputValue = document.querySelectorAll('.input-value').value;
inputValue = ''
},
deleteTodo: function(){
this.todos.shift( this.todo )
}
}
});
Your input value is two-way binded with todo.task so you could do the following after adding a new todo task.
this.todo.task = ''
Try to flush it like :
addNewTodo: function(e){
e.preventDefault();
this.todos.push( this.todo );
this.todo.task=""
}
Related
I'm a beginner with Vue and JS and I'm struggling to update the Vue instance's data attribute based on user input. This is what I have as the template for the Vue component (located inside taskTemplate variable):
<div>
<input type="text" placeholder="Insert your task" v-model="desc"/>
<p>{{ desc }}</p>
</div>
The Vue component is defined as follows:
Vue.component("task-item", {
props: {
id: Number,
desc: String,
},
template: taskTemplate
});
And this is populated in the HTML as follows:
<div id="task-list">
<task-item
v-for="item in taskList"
v-bind="item"
></task-item>
<div id="add-more">
<button v-on:click="newTask" type="button">Add a new task</button>
</div>
</div>
Where the task list is created with the Vue instance:
var app = new Vue({
el: "#task-list",
data: {
taskList: [
{ id: 1, desc: "" },
{ id: 2, desc: "" },
]
},
methods: {
newTask() {
this.taskList.push({ id: this.taskList.length + 1, desc: "" })
}
}
});
My problem is that after updating the input element in the webpage, the component's property gets updated, but if I type in the console app.taskList[0].desc it still returns an empty string.
The end goal is to send the data the user has introduced in an API call, so if I can access Vue components instead of the taskList within the data property it is still ok. I would like to know the best practices here as well.
Props shouldn't be used in a two-way binding. Instead, bind their value to input :value, and emit any changes to the parent component.
<div>
<input
type="text"
placeholder="Insert your task"
:value="desc"
#input="$emit('update:desc', $event.target.value)"
/>
<p>{{ desc }}</p>
</div>
In the parent component then you have to listen to update event and update the source value:
<div id="task-list">
<task-item
v-for="item in taskList"
:key="item.id" // do not forget about key
v-bind="item"
#update:desc="item.desc = $event"
// or you can use the sync modifier to listen to update events for all item's props.
// v-bind.sync="item"
></task-item>
<div id="add-more">
<button v-on:click="newTask" type="button">Add a new task</button>
</div>
</div>
Try to use :value and #input instead v-model :
Vue.component('task-item', {
template: `
<div>
<input type="text"
placeholder="Insert your task"
:value="desc"
#input="$emit('update', $event.target.value)"/>
<p>{{ desc }}</p>
</div>
`,
props: {
id: Number,
desc: String,
},
//template: taskTemplate
})
new Vue({
el: '#demo',
data: {
taskList: [
{ id: 1, desc: "" },
{ id: 2, desc: "" },
]
},
methods: {
newTask() {
this.taskList.push({ id: this.taskList.length + 1, desc: "" })
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<div id="task-list">
<task-item
v-for="(item, index) in taskList"
:key="index"
v-bind="item"
#update="item.desc = $event"
></task-item>
<div id="add-more">
<button v-on:click="newTask" type="button">Add a new task</button>
</div>
</div>
</div>
I'm making a simple shopping list app in Vue.js and I was curious if there's a standard way of doing what I need to do. I have a list of items with add and delete buttons:
const app = new Vue({
el: '#app',
data: {
items: [
'Chocolate',
'Pizza',
'Coca-Cola',
],
newItem: ''
},
template: `
<div>
<div>{{ items.length }} item{{ items.length !== 1 ? 's' : '' }}</div>
<ul>
<li v-for="(item, index) of items">
{{ item }}
<button #click="deleteItem(index)">X</button>
</li>
<li>
<input type="text" v-model="newItem" placeholder="Item name">
<button #click="addItem">+</button>
</li>
</ul>
</div>
`,
methods: {
addItem() {
const item = this.newItem.trim();
if (item === '') return;
this.items.push(item);
this.newItem = '';
},
deleteItem(index) {
this.items.splice(index, 1);
}
}
});
It works just as it should, but I'm not sure about using data entry that is never displayed anywhere. There's also another approach with $refs:
const app = new Vue({
el: '#app',
data: {
items: [
'Chocolate',
'Pizza',
'Coca-Cola',
],
},
template: `
<div>
<div>{{ items.length }} item{{ items.length !== 1 ? 's' : '' }}</div>
<ul>
<li v-for="(item, index) of items">
{{ item }}
<button #click="deleteItem(index)">X</button>
</li>
<li>
<input type="text" placeholder="Item name" ref="newItem">
<button #click="addItem($refs.newItem.value)">+</button>
</li>
</ul>
</div>
`,
methods: {
addItem(item) {
item = item.trim();
if (item === '') return;
this.items.push(item);
this.$refs.newItem.value = '';
},
deleteItem(index) {
this.items.splice(index, 1);
}
}
});
Instead of using separate data entry and v-model, I'm using $refs directly. Is any of these approaches more widely accepted in Vue.js community or guidelines? Or perhaps there's even more popular way?
I just wanted to share my views here. Personally I like to use v-model as it provides few added benefits like:
We can use .trim modifier with v-model which automatically trims whitespace from user input like:
<input v-model.trim="msg">
This way you don't need to write additional code to trim text like item = item.trim();. Few lines of code saved here.
Using this.newItem = '' we can easily clear out the previously entered text after button click using v-model reactivity feature. So, again less line of code instead of doing this.$refs.newItem.value = '';
Another advantage of using v-model is that, instead of doing
<button #click="addItem($refs.newItem.value)">
You can simply call the function like:
<button #click="addItem">
So, you can see these are the few benefits of using a simple v-model, which is mostly related to the developer experience (DX) point of view.
Working Demo:
const app = new Vue({
el: '#app',
data: {
items: [
'Chocolate',
'Pizza',
'Coca-Cola',
],
newItem: ''
},
template: `
<div>
<div>{{ items.length }} item{{ items.length !== 1 ? 's' : '' }}</div>
<ul>
<li v-for="(item, index) of items">
{{ item }}
<button #click="deleteItem(index)">X</button>
</li>
<li>
<input type="text" placeholder="Item name" v-model.trim="newItem">
<button #click="addItem">+</button>
</li>
</ul>
</div>
`,
methods: {
addItem() {
if (this.newItem === '') return;
this.items.push(this.newItem);
this.newItem = '';
},
deleteItem(index) {
this.items.splice(index, 1);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
<div id="app">
</div>
I'm playing with vue.js for learning purposes consisting of different components, one of them being a classic to do list. For now, everything is within one component.
I want to change the text of a button after it is clicked to hide an element from "hide" to "show" - I'm going about this by setting a text data object and then changing it in a function.
See below:
<div id="app">
<ul>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ul>
<input type="text" id="list-input">
<input type="submit" id="list-submit" v-on:click="addItem">
<span id="error" style="color: red; display: none;">Please Enter Text</span>
<ul>
<todoitem></todoitem>
<todoitem></todoitem>
<todoitem></todoitem>
</ul>
<h2 v-if="seen">SEEN</h2>
<button id="hide-seen" v-on:click="toggleSeen">{{ button.text }}</button>
</div>
<script type="text/javascript">
// components
Vue.component('todoitem', {
template: "<li>Test Item</li>"
})
// app code
var app = new Vue({
el: '#app',
data: {
todos: [
{ text: 'Sample Item 1' },
{ text: 'Sample Item 2' },
{ text: 'Sample Item 3' }
],
button: [
{ text: 'Hide'}
],
seen: true
},
methods: {
addItem: function() {
let item = document.getElementById("list-input").value;
let error = document.getElementById("error");
if (item == "") {
error.style.display = "block";
} else {
app.todos.push({ text: item });
error.style.display = "none";
}
},
toggleSeen: function() {
app.seen = false
app.button.push({ text: 'Show' });
}
}
})
</script>
Unexpectedly, the button is blank on both hide and show states. Being new to vue, this seems like a strange way to go about doing it. Is changing data in this context bad practice? I don't understand how to fix this, as I have no errors in my console.
Here you have your code in a snipplet.
I change your button by a plain object instead of an array and small adaptation in method toggleSeen.
// components
Vue.component('todoitem', {
template: "<li>Test Item</li>"
})
// app code
var app = new Vue({
el: '#app',
data: {
todos: [
{ text: 'Sample Item 1' },
{ text: 'Sample Item 2' },
{ text: 'Sample Item 3' }
],
button: {
text: 'Hide'
},
seen: true
},
methods: {
addItem: function() {
let item = document.getElementById("list-input").value;
let error = document.getElementById("error");
if (item == "") {
error.style.display = "block";
} else {
app.todos.push({ text: item });
error.style.display = "none";
}
},
toggleSeen: function() {
app.seen = !app.seen;
app.button.text = app.seen ? 'Hide' : 'Show';
}
}
});
<script src="https://vuejs.org/js/vue.min.js"></script>
<div id="app">
<ul>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ul>
<input type="text" id="list-input">
<input type="submit" id="list-submit" v-on:click="addItem">
<span id="error" style="color: red; display: none;">Please Enter Text</span>
<ul>
<todoitem></todoitem>
<todoitem></todoitem>
<todoitem></todoitem>
</ul>
<h2 v-if="seen">SEEN</h2>
<button id="hide-seen" v-on:click="toggleSeen">{{ button.text }}</button>
</div>
You can achieve this by using refs in vuejs:
<body>
<div id = 'app'>
<button #click="changeState" ref="btnToggle">Hide</button>
<div v-show="show">
<h1>1 to 100</h1>
<p v-for="i in 100">{{i}}</p>
</div>
</div>
<script>
const app = new Vue({
el:'#app',
data: function(){
return{
show: true
}
},
methods: {
changeState: function(){
this.show = !this.show;
this.$refs.btnToggle.innerText = this.show?'Hide':'Show';
}
},
});
</script>
</body>
How to use the computed to realize It? When I input in the input the ul only show the li which inclue my input. For example, if I am inputting the Ad, then only show the "Add some todos" li. And if I give up input, the ul will goback, all the li will show again.
This is my code, and I use Vue.
div id="app">
<input v-model="newTodo" v-on:keyup.enter="addTodo">
<ul>
<li v-for="todo in todos">
<span>{{ todo.text }}</span>
<button v-on:click="removeTodo($index)">X</button>
</li>
</ul>
<script>
new Vue({
el: '#app',
data: {
newTodo: '',
todos: [
{text: 'Add some todos'}
]
},
methods: {
addTodo: function () {
var text = this.newTodo.trim()
if (text) {
this.todos.push({text: text})
this.newTodo = ''
}
},
removeTodo: function (index) {
this.todos.splice(index, 1)
}
}
})
</script>
Define a computed to do the filtering you want done.
Use the computed instead of todos in your v-for.
new Vue({
el: '#app',
data: {
newTodo: '',
todos: [{
text: 'Add some todos'
}]
},
computed: {
filteredTodos: function() {
const re = new RegExp(this.newTodo, 'i');
return this.todos.filter((item) => re.test(item.text));
}
},
methods: {
addTodo: function() {
var text = this.newTodo.trim()
if (text) {
this.todos.push({
text: text
})
this.newTodo = ''
}
},
removeTodo: function(index) {
this.todos.splice(index, 1)
}
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
<div id="app">
<input v-model="newTodo" v-on:keyup.enter="addTodo">
<ul>
<li v-for="todo in filteredTodos">
<span>{{ todo.text }}</span>
<button v-on:click="removeTodo($index)">X</button>
</li>
</ul>
</div>
I have watched Jeffory's series on Vue.js and I'm practicing writing my own components using the vueify and browserify with gulp. Even after following along with the video I can't manage to get it to render properly. I keep getting this error.
TRY NUMBER ONE
Error:
Attribute "list" is ignored on component <alert> because the component is a fragment instance:
The view:
<div id = "app" class = "container">
<alert :list = "tasks"></alert>
</div>
The Componet:
<template>
<div>
<h1>My tasks
<span v-show = "remaining"> ( #{{ remaining }} )</span>
</h1>
<ul>
<li :class = "{ 'completed': task.completed }"
v-for = "task in list"
#click="task.completed = ! task.completed"
>
#{{ task.body }}
</li>
</ul>
</div>
</template>
<script>
export default {
props: ['list'],
computed: {
remaining: function() {
return this.list.filter(this.inProgress).length;
}
},
methods: {
isCompleted: function(task) {
return task.completed;
},
inProgress: function(task) {
return ! this.isCompleted(task);
}
}
}
new Vue({
el: '#demo',
data: {
tasks: [
{ body: 'go to the store', completed: false },
{ body: 'go to the bank', completed: false },
{ body: 'go to the doctor', completed: true }
]
},
methods: {
toggleCompletedFor: function(task) {
task.completed = ! task.completed;
}
}
});
</script>
It gives me a link to read the Fragement Instance section in the documentation. What I understood was that if the template is composed of more than one top level element the component will be fragmented. So I took everything out of the template execpt the actual li tags. With this I still get the same error. What am missing?
Edited Template:
<li :class = "{ 'completed': task.completed }"
v-for = "task in list"
#click="task.completed = ! task.completed"
>
#{{ task.body }}
</li>
TRY NUMBER TWO
Same error
View
<div id ="app">
<alert>
<strong>Success!</strong> Your shit has been uploaded!
</alert>
<alert type = "success">
<strong>Success!</strong> Your shit has been uploaded!
</alert>
<alert type = "error">
<strong>Success!</strong> Your shit has been uploaded!
</alert>
</div>
Main.js
var Vue = require('vue');
import Alert from './componets/Alert.vue';
new Vue({
el: '#app',
components: { Alert },
ready: function() {
alert('Ready to go!');
}
});
Alert.Vue
<template>
<div>
<div :class ="alertClasses" v-show = "show">
<slot></slot>
<span class = "Alert_close" #click="show = false">X</span>
</div>
</div>
</template>
<script>
export default {
props: ['type'],
data: function() {
return {
show: true
};
},
computed: {
alertClasses: function () {
var type = this.type;
return{
"Alert": true,
"Alert--Success": type == "success",
"Alert--Error": type == "error"
}
}
}
}
</script>
Fresh re-install of the most curruent versions of node,gulp and vueify turned out to be the solution.