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>
Related
Everything here displays and acts how i want except for my call to the setInputName function. I believe the reason for this is because that happens within the tabs component which is built on a separate component and template, which then uses another component/template tab for the individual list item tabs.
The problem here is that when I click my list items, the console prints that _vm.setInputName is not a function
How can I fix this to be able to call this function from within the rendered template?
<tabs>
<tab name="Activity" :selected="true">
<div class="row notesInput" id="notesInput">
<div class="col-lg-12">
<div class="tabs">
<ul style="border-bottom:none !important; text-decoration:none">
<li v-on:click="setInputName('public')">Public</li>
<li v-on:click="setInputName('public')">Internal</li>
</ul>
</div>
<div>
<input type="text" v-bind:name="inputName">
<br>
Input name is: {{ inputName }}
</div>
</div>
</div>
</tab>
</tabs>
<script>
Vue.component('tabs', {
template: `
<div>
<div class="tabs">
<ul>
<li v-for="tab in tabs" :class="{ 'is-active': tab.isActive }">
<a :href="tab.href" #click="selectTab(tab)">{{ tab.name }}</a>
</li>
</ul>
</div>
<div class="tabs-details">
<slot></slot>
</div>
</div>
`,
data() {
return {
tabs: [],
};
},
created() {
this.tabs = this.$children;
},
methods: {
selectTab(selectedTab) {
this.tabs.forEach(tab => {
tab.isActive = tab.name == selectedTab.name;
});
} } });
Vue.component('tab', {
template: `
<div v-show="isActive"><slot></slot></div>
`,
props: {
name: { required: true },
selected: { default: false } },
data() {
return {
isActive: false,
};
},
computed: {
href() {
return '#' + this.name.toLowerCase().replace(/ /g, '-');
} },
mounted() {
this.isActive = this.selected;
} });
export default {
components: {
Multipane,
MultipaneResizer,
},
data () {
return {
inputName: '',
}
},
computed: {
},
methods: {
setInputName(str) {
this.inputName = str;
}
};
</script>
I think the issue is because you are calling the function from another component.
but your function is in another function. If you want this to work then you should extend your component in which your function exists and then you can use it.
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=""
}
I have a todo list in vue and I'm using pop() to clear out/delete list items. See the code in question below:
// 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";
}
},
removeItem: function() {
this.todos.pop();
},
toggleSeen: function() {
app.seen = !app.seen;
app.button.text = app.seen ? 'Hide' : 'Show';
}
}
});
.todo-list {
list-style-type: square;
}
.todo-list__delete {
display: none;
}
li:hover .todo-list__delete {
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
<ul class="todo-list">
<li v-for="todo in todos">
{{ todo.text }}
<a v-on:click="removeItem" class="todo-list__delete" href="#">Delete</a>
</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>
</ul>
<h2 v-if="seen">SEEN</h2>
<button id="hide-seen" v-on:click="toggleSeen">{{ button.text }}</button>
</div>
The expected behavior is that when delete is clicked, it invokes the removeItem function and with the usage of this in that function it deletes the selected item. However what actually happens is it just deletes nodes starting from the bottom.
I thought the issue is that with this I'm actually referencing the delete link and not actually the <li> element I'm trying to remove. So I tried both:
removeItem: function() {
this.todos.parentElement.pop();
}
And:
removeItem: function() {
this.parentElement.todos.pop();
}
With no luck.
How does this work in Vue?
In that context, this refers to the Vue component (not the DOM element). this.todos is the todos array inside the component's data object, and pop removes the last item of an array. So that's why the last element is being removed.
If you want to remove a specific element you'll need to pass some information to the removeItem function about which element you want removed and then have removeItem() drop that specific element from the todos array instead of popping the last element. One simple way to do this would be to pass the array index to the removeItem function, and then splice that index out of the todos array:
<li v-for="(todo, index) in todos">
...
<a v-on:click="removeItem(index)">Delete</a>
</li>
removeItem: function(index) {
this.todos.splice(index, 1);
},
Your full snippet with that change applied is below:
// 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";
}
},
removeItem: function(index) {
this.todos.splice(index, 1);
},
toggleSeen: function() {
app.seen = !app.seen;
app.button.text = app.seen ? 'Hide' : 'Show';
}
}
});
.todo-list {
list-style-type: square;
}
.todo-list__delete {
display: none;
}
li:hover .todo-list__delete {
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
<ul class="todo-list">
<li v-for="(todo, index) in todos">
{{ todo.text }}
<a v-on:click="removeItem(index)" class="todo-list__delete" href="#">Delete</a>
</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>
</ul>
<h2 v-if="seen">SEEN</h2>
<button id="hide-seen" v-on:click="toggleSeen">{{ button.text }}</button>
</div>
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.