Vue.js 2 bind a class in a variable - javascript

I`m using the Vue.js 2 and Laravel 5.3 to build a web.
When I click the ajaxbtn, the class do not bind in the variable, Any idea?
*Here is the html.
<div id="root" class="container">
<ajaxbtns>
<ajaxbtn name="get taiwanstay" url="api/taiwanstay" :selected="true" ></ajaxbtn>
<ajaxbtn name="get itwyp" url="api/itwyp" ></ajaxbtn>
</ajaxbtns>
</div>
*Here is the script
Vue.component('ajaxbtns',{
template:
`
<div class="tabs">
<ul>
<slot></slot>
</ul>
</div>
`,
data : function () {
return {
allAjaxBtn : []
};
},
created: function () {
this.allAjaxBtn = this.$children;
}
});
Vue.component('ajaxbtn',{
template:
`
<li :class="{ 'is-active' : btnActive }">
<a #click="ajaxbtnClick(name)" href="#" >#{{ name }}</a>
</li>
`,
props : {
name: { required: true },
url : { required: true },
selected: { default : false }
},
data :function () {
return {
btnActive : false
}
},
created: function () {
this.btnActive = this.selected;
},
methods : {
ajaxbtnClick : function (name) {
this.$parent.allAjaxBtn.forEach( btn => {
this.btnActive = (btn.name == name);
});
}
}
});
new Vue({
el: '#root'
});

The issue is not with the binding of the variable, that works fine. The problem is that btnActive will change for each iteration. You may get lucky in the case that the last btn matches, which would set it to true. However, if the condition was met earlier, it would be switched from true to false anyway.
Instead, conditionalize your query and then assign the btn to the btnActive:
if (btn.name == name) {
this.btnActive = btn;
}

Related

How to bind value to each list item in Vue to toggle the images

I'm trying to toggle between the 2 images by triggering a function on click event, as shown below, how do I apply the same idea to different list items to toggle the images individually for each item? right now, all the list items same image because of one global value i.e val
var app = new Vue({
el: '#app',
data: function() {
return {
val:true,
selectedImg:"",
checked: "#/assets/images/check.png",
unchecked: "#/assets/images/uncheck.png",
items: [
{ message: 'laundry' },
{ message: 'cooking' },
{ message: 'cleaning'}
]
}
},
methods: {
myfunc () {
this.val = (this.val === true ? false : true)
if(this.val === true) {
this.selectedImg = this.checked
} else {
this.selectedImg = this.unchecked
}
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul>
<li v-for="item in items" :key="item.message">
<button #click="myfunc"><img :src="selectedImg"/></button>
{{ item.message }}
</li>
</ul>
</div>
You should move selectedImg and val into items objects and pass item to myfunc function.
You can also look at the answer in codepen.
var app = new Vue({
el: '#app',
data: function() {
return {
checked: "#/assets/images/check.png",
unchecked: "#/assets/images/uncheck.png",
items: [
{ message: 'laundry', selectedImg:"" , val:true},
{ message: 'cooking', selectedImg:"", val:true},
{ message: 'cleaning', selectedImg:"", val:true}
]
}
},
methods: {
myfunc (item) {
item.val = (item.val === true ? false : true)
if(item.val === true) {
item.selectedImg = this.checked
} else {
item.selectedImg = this.unchecked
}
},
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<ul>
<li v-for="item in items" :key="item.message">
<button #click="myfunc(item)"><img :src="item.selectedImg"/></button>
{{ item.message }}
</li>
</ul>
</div>

How can I filter object with VueJS?

I've been practicing Vue.js recently.
Right now, I am making a simple todo list.
Some buttons does't work which filter tasks.
All button for listing every single tasks.
ongoing button is for uncheck input checkbox square.
done button is for checked ones.
Does anybody know how to fix?
new Vue({
el: '#app',
data: {
inputTask:'',
todos: [
{task:'task01', done: false},
{task:'task02', done: false},
{task:'task03', done: true},
{task:'task04', done: true},
{task:'task05', done: false},
]
},
computed: {
},
methods: {
allTask : function () {
this.todos
},
ongoingTask: function () {
this.todos = this.todos.filter(function (el) {
el.value = false
})
},
doneTask: function () {
this.todos = this.todos.filter(function (el) {
el.checked
})
},
addTask: function() {
const todo = { task: this.inputTask, done:false, };
this.todos.push(todo);
this.inputTask ='';
},
deleteTask: function (index) {
this.todos.splice(index, 1);
}
}
})
.done {text-decoration: line-through;}
<div id="app">
<input type="text" v-model="inputTask">
<button #click="addTask">Add</button>
<div>
<button #click="allTask">All</button>
<button #click="ongoingTask">Ongoing</button>
<button #click="doneTask">Done</button>
</div>
<ul>
<li v-for="(todo, index) in todos" :key="todo">
<input type="checkbox" v-model="todo.done">
<span :class="{done:todo.done}">{{todo.task}}</span>
<button #click="deleteTask(index)">Remove</button>
</li>
</ul>
<p v-show="todos.length===0">Nothing to do</p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
You should use computed property to filter your task. And i guess you can add some improvements for user friendly interface. So your code will look like this:
new Vue({
el: '#app',
data: {
type: '',
inputTask:'',
todos: [
{task:'task01', done: false},
{task:'task02', done: false},
{task:'task03', done: true},
{task:'task04', done: true},
{task:'task05', done: false},
]
},
methods: {
setFilterType(type) {
this.type = type;
},
addTask() {
const todo = { task: this.inputTask, done: false};
this.todos.push(todo);
this.inputTask = '';
},
deleteTask(index) {
this.todos.splice(index, 1);
}
},
computed: {
filteredTodos() {
return this.todos.filter(todo => {
switch(this.type) {
case 'ongoing':
return !todo.done;
case 'done':
return todo.done;
default:
return true;
}
});
}
}
})
And template:
<div id="app">
<input type="text" v-model="inputTask">
<button #click="addTask">Add</button>
<div>
<button :class="{active: type ===''}" #click="setFilterType('')">All</button>
<button :class="{active: type ==='ongoing'}" #click="setFilterType('ongoing')">Ongoing</button>
<button :class="{active: type ==='done'}" #click="setFilterType('done')">Done</button>
</div>
<ul>
<li v-for="(todo, index) in filteredTodos" :key="todo">
<input type="checkbox" v-model="todo.done">
<span :class="{done:todo.done}">{{todo.task}}</span>
<button #click="deleteTask(index)">Remove</button>
</li>
</ul>
<p v-show="todos.length===0">Nothing to do</p>
</div>
Also i've added active class to button. So you can highlight via css current sorting.
More about computed you can read here
All your filter function on ongoingTask and doneTask are wrong, they should return a value; take a look at how filter function works: here.
I'm not sure what's the intended behavior of ongoingTaks is, but I guess the following should solve the problem. Please let me know and I'll update the answer correspondingly.
// ...
ongoingTask: function () {
this.todos = this.todos.filter(function (el) {
return el.value === false
})
},
doneTask: function () {
this.todos = this.todos.filter(function (el) {
return el.checked
})
},
// ...
EDIT:
The other part of the issue had to do with the logic, as soon as you clicked on any button that triggers the filtering, data in the state is removed (for instance, if I'm in the default view, viewing all todos, when I click in the Ongoing button, all the done TODO in the list gets removed and forever lost!); ideally you should keep that data and use computed properties to tackle the issue, here you have a working example (I'm using a Vue component here just for the sake of condensing all data in one place):
<template>
<div id="app">
<input type="text" v-model="inputTask">
<button #click="addTask">Add</button>
<div>
<button #click="changeFilter('all')">All</button>
<button #click="changeFilter('ongoing')">Ongoing</button>
<button #click="changeFilter('done')">Done</button>
</div>
<ul>
<li v-for="(todo, index) in getTasksList()" :key="{index}">
<input type="checkbox" v-model="todo.done">
<span :class="{done:todo.done}">{{todo.task}}</span>
<button #click="deleteTask(index)">Remove</button>
</li>
</ul>
<p v-show="todos.length===0">Nothing to do</p>
</div>
</template>
<script>
export default {
name: "App",
data: () => ({
inputTask: "",
currentFilter: "ongoing",
todos: [
{ task: "task01", done: false },
{ task: "task02", done: false },
{ task: "task03", done: true },
{ task: "task04", done: true },
{ task: "task05", done: false }
]
}),
computed: {
ongoingTasks: function() {
return this.todos.filter(function(todo) {
return todo.done === false;
});
},
doneTasks: function() {
return this.todos.filter(function(el) {
return el.done;
});
}
},
methods: {
changeFilter(newFilter) {
this.currentFilter = newFilter;
},
addTask: function() {
const todo = { task: this.inputTask, done: false };
this.todos.push(todo);
this.inputTask = "";
},
deleteTask: function(index) {
this.todos.splice(index, 1);
},
getTasksList() {
switch (this.currentFilter) {
case "ongoing":
return this.ongoingTasks;
case "done":
return this.doneTasks;
case "all":
default:
return this.todos;
}
}
}
};
</script>
I hope this helps!

Click-and-edit text input with Vue

I'm looking for a click-and-edit Vue component.
I've found a fiddle and made some edits. It works like this:
The fiddle is here.
The problem: I need an additional click to make the input focused. How can I make it focused automatically?
The code from the fiddle. HTML:
<div id="app">
Click the values to edit!
<ul class="todo-list">
<li v-for = "todo in todos">
<input v-if = "todo.edit" v-model = "todo.title"
#blur= "todo.edit = false; $emit('update')"
#keyup.enter = "todo.edit=false; $emit('update')">
<div v-else>
<label #click = "todo.edit = true;"> {{todo.title}} </label>
</div>
</li>
</ul>
</div>
JS:
new Vue({
el: '#app',
data: {
todos: [{'title':'one value','edit':false},
{'title':'one value','edit':false},
{'title':'otro titulo','edit':false}],
editedTodo: null,
message: 'Hello Vue.js!'
},
methods: {
editTodo: function(todo) {
this.editedTodo = todo;
},
}
})
You can use a directive, for example
JS
new Vue({
el: '#app',
data: {
todos: [
{ title: 'one value', edit: false },
{ title: 'one value', edit: false },
{ title: 'otro titulo', edit: false }
],
editedTodo: null,
message: 'Hello Vue.js!'
},
methods: {
editTodo: function (todo) {
this.editedTodo = todo
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
})
HTML
<div id="app">
Click the values to edit!
<ul class="todo-list">
<li v-for="todo in todos">
<input
v-if="todo.edit"
v-model="todo.title"
#blur="todo.edit = false; $emit('update')"
#keyup.enter="todo.edit=false; $emit('update')"
v-focus
>
<div v-else>
<label #click="todo.edit = true;"> {{todo.title}} </label>
</div>
</li>
</ul>
</div>
You can find more info here
https://v2.vuejs.org/v2/guide/custom-directive.html
With #AitorDB's help I have written a Vue component for this, I call it Click-to-Edit. It is ready to use, so I'm posting it.
What it does:
Supports v-model
Saves changes on clicking elsewhere and on pressing Enter
ClickToEdit.vue: (vue 2.x)
<template>
<div>
<input type="text"
v-if="edit"
:value="valueLocal"
#blur.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
#keyup.enter.native="valueLocal = $event.target.value; edit = false; $emit('input', valueLocal);"
v-focus=""
/>
<p v-else="" #click="edit = true;">
{{valueLocal}}
</p>
</div>
</template>
<script>
export default {
props: ['value'],
data () {
return {
edit: false,
valueLocal: this.value
}
},
watch: {
value: function() {
this.valueLocal = this.value;
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
}
</script>
Edit for 3.x: [Breaking changes between 2.x and 3.x]
remove .native from the event handlers
change the focus hook to mounted as described in Custom Directives 3.x.
Built on #Masen Furer's work. I added some protection to handle when a user deletes all of the data. There is probably a way to accomplish this using "update" but I couldn't get it working.
I also added the ability to hit escape and abandon any changes.
<template>
<span>
<input type="text"
v-if="edit"
:value="valueLocal"
#blur="save($event);"
#keyup.enter="save($event);"
#keyup.esc="esc($event);"
v-focus=""/>
<span v-else #click="edit = true;">
{{valueLocal}}
</span>
</span>
</template>
<script>
export default {
props: ['value'],
data () {
return {
edit: false,
valueLocal: this.value,
oldValue: (' ' + this.value).slice(1)
}
},
methods: {
save(event){
if(event.target.value){
this.valueLocal = event.target.value;
this.edit = false;
this.$emit('input', this.valueLocal);
}
},
esc(event){
this.valueLocal = this.oldValue;
event.target.value = this.oldValue;
this.edit = false;
this.$emit('input', this.valueLocal);
}
},
watch: {
value: function() {
this.valueLocal = this.value;
}
},
directives: {
focus: {
inserted (el) {
el.focus()
}
}
}
}
</script>

Vuejs v-model overwritting array

I am using Vuejs 2 with laravel. Currently i was working with select boxes for a permissions module and it is a box complex structure. The issue i am having is when i try to bind my nested array item in v-model it acts as string. everytime i check a box it overwrites the variable.
workspace.selectedOperations is the model that is overwriting.
This is the html for it :
<b-tab v-for="workspace in addedWorkspaces" :key="workspace.id" :title="workspace.title" active>
<div class="roles-permissions">
<label>Permissions</label>
<div class="permissions-path">
<ul class="pl-0">
<li v-for="entity in workspace.entities">
<b-form-checkbox>{{entity.title}}</b-form-checkbox>
<ul>
<li v-for="operation in entity.operations">{{workspace.selectedOperations}}
<b-form-checkbox v-model="workspace.selectedOperations" :value="operation.id">{{operation.title}}</b-form-checkbox>
</li>
</ul>
</li>
</ul>
</div>
</div>
</b-tab>
This is the script :
<script>
import Multiselect from 'vue-multiselect'
export default {
props : ['showModalProp'],
data () {
return {
title : '',
value: [],
entities : [],
addedWorkspaces : [],
selectedWorkspaces : '',
}
},
methods: {
getOperationsList(){
this.$http.get('entity').then(response=>{
response = response.body.response;
this.entities = response.data;
});
},
showModal() {
this.$refs.myModalRef.show()
},
hideModal() {
this.$refs.myModalRef.hide()
},
onHidden(){
this.$emit('HideModalValue');
},
addWorkspaces(){
this.addedWorkspaces = this.selectedWorkspaces;
this.selectedWorkspaces = [];
for (var i = this.addedWorkspaces.length - 1; i >= 0; i--) {
this.addedWorkspaces[i].entities =
JSON.parse(JSON.stringify(this.entities));
this.addedWorkspaces[i].selectedOperations = [];
}
}
},
mounted(){
this.getOperationsList();
},
components: {
Multiselect
},
watch:{
showModalProp(value){
if(value){
this.showModal();
}
if(!value){
this.hideModal();
}
}
},
computed :{
options(){
return this.$store.getters.getWorkspaces;
}
}
}
</script>
if you want v-model of b-form-checkbox to be an array,you should wrap your b-form-checkbox by b-form-checkbox-group and set v-model on it
see https://bootstrap-vue.js.org/docs/components/form-checkbox

How open first tab with vuejs?

How do I open the first tab with VueJS? I have made two components but I don't know how to open the first tab automatically. This is what I've tried, but it doesn't work:
In mounted when i do console.log(this.tabs) return only 0 but before i do this.tabs = this.$children
Tabs.vue
<template>
<div>
<div class="tabs-header">
<ul>
<li v-for="tab in tabs" :key="tab.id" :class="{'is-active' : tab.isActive}">
<a :href="tab.href" #click="selectTab(tab)">{{ tab.name }}</a>
</li>
</ul>
</div>
<div class="content">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "Tabs",
data: function(){
return { tabs: null };
},
created: function() {
this.tabs = this.$children;
//this.tabs[0].isActive = true;
},
methods: {
selectTab (selectedTab){
this.tabs.forEach(tab => {
tab.isActive = (tab.href == selectedTab.href);
});
}
}
}
</script>
This is my second components $children
Tab.vue
<template>
<div v-show="isActive">
<slot></slot>
</div>
</template>
<script>
export default {
name: "Tabs",
props: {
name: { require: true },
selected: { default: false }
},
data() {
return { isActive: false }
},
computed: {
href() {
return '#' + this.name.toLowerCase().replace(/ /g,'-');
}
},
mounted() {
this.isActive = this.selected;
},
methods: {
select(){
this.isActive = true;
}
}
}
</script>
This line doesn't work:
//this.tabs[0].isActive = true;
I copy pasted the code from laracasts.com/series/learn-vue-2-step-by-step/episodes/11 and the first tab is opened by default.
It is the case because in the html, you have :selected=true set on the first tab.
If you want to open the second tab by default, move :selected=true to the second tab, like this : https://jsfiddle.net/ahp3zzte/1/
If you want to change the default tab dynamically, remove :selected=true from the html and call the selectTab method in the js. Also note that to do this, you need to use mounted instead of created. Check this other fiddle : https://jsfiddle.net/0402y2ew/

Categories