When I toggle my variable active in the code below, the active CSS class is applied/removed from all elements in the list. How do I target the list elements individually? The Todo-List example has similar functionality (with todo / todo completed), but it's a bit beyond my skill set.
<ul>
<li v-bind:class="{ active: active }" v-on:click="toggleActive">Test 1</li>
<li v-bind:class="{ active: active }" v-on:click="toggleActive">Test 2</li>
<li v-bind:class="{ active: active }" v-on:click="toggleActive">Test 3</li>
</ul>
toggleActive: function() {
this.active = !this.active;
}
One way to do it is to store each state inside an item object. So you would need to create an array of items, and each item would have the structure:
{
text: "item #"
active: true
}
Note I'm passing the item reference when calling the toggleActive method.
<li v-for="item in items"
v-bind:class="{ active: item.active }"
v-on:click="toggleActive(item)">
{{ item.text }}
</li>
toggleActive: function(item) {
item.active = !item.active;
console.log(item);
}
Here a working example: https://jsfiddle.net/pkwroL5L/1/
Hope it help!
If you want to toggle the clasess of individual elements programattically by VueJS
VueJS allows you to bind the class of the anchor tag (e.g) directly to the index of the li element so that when the vuejs variable bound to the index changes, the class also changes. Check these two links for more details
This is the crux of the solution
:class="{current:i == current}
available on the fiddle below and another post that explains in blog format how anchor class can be dynamically controlled in vuejs
https://jsfiddle.net/Herteby/kpkcfcdw/
https://stackoverblow.wordpress.com/2021/04/03/how-modern-javascript-makes-click-simulation/
Related
This is probably a really naive question that is less about vue-drag-drop and more about vuejs, which I'm new to.
If I have two lists of stuff:
<ul>
<li v-for="thing in thing">
<drag :transfer-data="{ thing }">
<span>{{ thing.title }}</span>
</drag>
</li>
</ul>
<ul>
<li v-for="day in days">
<drop #drop="handleDrop" ref="day"></drop?
</li>
</ul>
In the handleDrop() method I can see the event, which include what was dragged into the list item, but I don't see how I have any context on which item in the array the dragged thing was dragged into. I tried using a ref on the drop element, but that didn't seem to be what I wanted.
How do I know which day the item was dragged into?
I figured out I need to pass the data myself. One way to do this is to wrap the function vue-drag-drop provides.
<ul>
<li v-for="day in days">
<drop #drop="function(data, event) { handleDrop(data, day, event); }" ref="day"></drop>
</li>
</ul>
That seems to work as expected, but I'm wondering if there's another way to do it like using a library attribute (similar to ref).
Maybe I missed something, but wouldn't this in handleDrop() be the component that was dropped onto? That's how it is for other event handlers in Vue.
See here for an example:
<div id="example-2">
<!-- `greet` is the name of a method defined below -->
<button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
el: '#example-2',
data: {
name: 'Vue.js'
},
// define methods under the `methods` object
methods: {
greet: function (event) {
// `this` inside methods points to the Vue instance
alert('Hello ' + this.name + '!')
// `event` is the native DOM event
if (event) {
alert(event.target.tagName)
}
}
}
})
// you can invoke methods in JavaScript too
example2.greet() // => 'Hello Vue.js!'
Update
The above is true, but you want to know which day is that drop component. To achieve that, pass it as a prop to the child drop component like so:
Vue.component('drop', {
props: ['day'],
// ...
})
<ul>
<li v-for="day in days">
<drop #drop="handleDrop" ref="day" :day="day"></drop>
</li>
</ul>
The :day="day" is the key. Then, in handleDrop(), this.day will be the day
I have this component which is managed by Bootstrap, specifically one of those nav-tabs widgets where, as you click Bootstrap shows and hides.
Bootstrap keeps track of which item was clicked on using the .active class. And, in Vue, I was to initialize a certain nav as being active on page load. But, once that's done, I want Vue leave the .active class management entirely up to Bootstrap.
<template>
<li class="nav-item" v-if="toshow">
<a class="nav-link" v-bind:id="'nav_' + link"
:class="{ active: isActive }" :aria-expanded="isActive"
v-bind:href="'#'+link" data-toggle="tab" #click="onclick">
{{label_}}
<span v-if="badge" class="badge" :class="badge_level">{{badge}}</span>
<span v-if="dynamic_badge" class="badge" :class="badge_level" >{{badge_value}}</span>
</a>
</li>
</template>
At page load time, each component checks against Vuex and figures out if its id is in this.$store.state.active_tab - that's what sets .active.
,isActive: function(){
//active_tab is where I specify which tab should be active
//at first
var res = this.link === this.$store.state.active_tab;
return res;
},
v-once is not a good fit, because the only thing I want to disable is the computation of .active (the badge children need to be updated live).
The component works, kinda. I think mostly because this.$store.state.active_tab's value does not mutate so Vue doesn't re-render. But it seems brittle at best.
What are best practices for using Vue to only set the initial values of certain variables, and then relinquishing control, without using v-once?
I would just access the a.nav-link element and add the .active class to its classList directly.
You can add a ref attribute to the a.nav-lank element link so:
<a class="nav-link" ref="link" ...>
...
</a>
And then add the .active class in the mounted hook:
mounted() {
if (this.link === this.$store.state.active_tab) {
this.$refs.link.classList.add('active');
}
}
I have vue event attached to elements that are looped.
I'm having challenge trying display CRUD action on an item, instead, all the looped items display their individual CRUD
How can I make it unique to an element? any vue event modifier for this?
Below is my code
<i class="material-icons">list</i>
<div v-if="showButtons">
<ul>
<li>Edit</li>
<li>Delete</li>
<li>Stop</li>
</ul>
</div>
The showIcons method below
showIcons: function () {
this.showButtons = true
}
since you are binding showButtons property to all your looped items, when you mouse over an item theshowButtonsis toggled true and all the items bound to showButtons are displayed.
So you need to use a unique identifier to decide whether the buttons for an item should be displayed or not.
You might be looping using v-for so you can make use of index.
template
<div v-for="(item , index)">
<i class="material-icons">list</i>
<div v-if="currentlyShowing === index">
<ul>
<li>Edit</li>
<li>Delete</li>
<li>Stop</li>
</ul>
</div>
</div>
script
data(){
return{
currentlyShowing: null
}
},
methods:{
showIcons: function (index) {
this.showButtons = true
this.currentlyShowing = index;
}
}
I have a component named controls:
<li class="controls__item" v-if="options[0].save == 'show'">
<button class="btn" :options[0].saveAttr>Save</button>
</li>
I'm having trouble rendering an attribute defined in the options property:
<controls :options='[{ save: "show", saveAttr: "sampleAttr='0' "}]'></controls>
This is what I'm trying to achieve:
<button class="btn" sampleAttr='0'>Save</button>
That's not the correct syntax for binding in Vue.
If the name of the attribute to bind to is never going to change, you should specify the name in the controls component:
<li class="controls__item" v-if="options[0].save == 'show'">
<button class="btn" :sampleAttr="options[0].saveAttr">Save</button>
</li>
And just change the options to pass in a value for saveAttr:
<controls :options='[{ save: "show", saveAttr: "0" }]'></controls>
If the name of the attribute (or the number of attributes) could change, then you should pass an object to the v-bind directive like so:
<li class="controls__item" v-if="options[0].save == 'show'">
<button class="btn" v-bind="options[0].saveAttrs">Save</button>
</li>
And then pass in an object for saveAttrs:
<controls :options='[{save : "show", saveAttrs: { sampleAttr: 0 }]'></controls>
Let's start with your testdata (just a little clean up) let's say you have two buttons since it seems like you want to do that later on. I'm not yet sure what the save : "show" is supposed to do - so I do my best to give a flexible example.
[{
'text': 'Save',
'click': function() { alert('save'); }
,{
'text': 'Delete',
'click': function() { alert('delete'); }
}]
Not lets say you have that testdata in your component called "controls"
<controls :options="[{'text': 'Save','click': function() { alert('save'); },{'text': 'Delete','click': function() { alert('delete'); }}]"> </controls>
As we can see your controls has an property called options. So your code for your component should look like:
<template>
<div class="controls">
<li class="controls__item" v-for="control in options">
<button class="btn" #click="control.click">{{ control.text }}</button>
</li>
</div>
</template>
<script>
export default {
props: ['options']
}
</script>
You need to define the prop you want to bind on the component (options). Options is now bound according to our test date. Since it's an array we can use v-for to loop through it. We then bind the given text as button content and the given click function as on click event.
I hope this helps.
I have a nested for ... in loop in vue js. What I'm trying to to is to skip elements if the value of the element is null. Here is the html code:
<ul>
<li v-for="item in items" track-by="id">
<ol>
<li v-for="child in item.children" track-by="id"></li>
</ol>
</li>
</ul>
null elements may be present in both item and item.children objects.
For example:
var data = {
1: {
id: 1,
title: "This should be rendered",
children: {
100: {
id: 100,
subtitle: "I am a child"
},
101: null
}
},
2: null,
3: {
id: 3,
title: "Should should be rendered as well",
children: {}
}
};
With this data data[1].children[101] should not be rendered and if data[1].children[100] becomes null later it should be omitted from the list.
P.S. I know this is probably not the best way to represent data but I'm not responsible for that :)
Edit: Actually, a simple v-if might work:
<li v-for="item in items" v-if="item !== null" track-by="id">
Give it a try. If not, do this:
You can add a filter for that (in main.js before App instance):
Vue.filter('removeNullProps',function(object) {
// sorry for using lodash and ES2015 arrow functions :-P
return _.reject(object, (value) => value === null)
})
then in the template:
<li v-for="item in items | removeNullProps" track-by="id">
<ol>
<li v-for="child in item.children | removeNullProps" track-by="id"></li>
</ol>
</li>
In Vue 2, filters have been deprecated in v-fors.
Now you should use computed properties. Demo below.
new Vue({
el: '#app',
data: {
items: [
'item 1',
'item 2',
null,
'item 4',
null,
'item 6'
]
},
computed: {
nonNullItems: function() {
return this.items.filter(function(item) {
return item !== null;
});
}
}
})
<script src="https://unpkg.com/vue#2"></script>
<div id="app">
Using a computed property:
<ul>
<li v-for="item in nonNullItems">
{{ item }}
</li>
</ul>
<hr>
Using v-if:
<ul>
<li v-for="item in items" v-if="item !== null">
{{ item }}
</li>
</ul>
</div>
I would advise you against using v-if and v-for in the same element. What I found worked and didn't affect performance is this :
<li v-for="(value, key) in row.item.filter(x => x !== null)" :key="key"{{value}}</li>
You just need to run the filter function on the array you are going through. This is a common use in c# and found it was no different in JavaScript. This will basically skip the nulls when iterating.
Hope this helps (3 years later).
Just use v-if to do with it. But the first, do not use track-by="id" because of the null item and null child. You can check the demo here https://jsfiddle.net/13mtm5zo/1/.
Maybe the better way is to deal with the data first before the render.
VueJs style guide tell us to :
"Never use v-if on the same element as v-for."
How to handle v-if with v-for properly according to the vue style guide :
<ul v-if="shouldShowUsers">
<li
v-for="user in users"
:key="user.id"
>
{{ user.name }}
</li>
</ul>
See more on how to handle v-if with v-for here : https://v2.vuejs.org/v2/style-guide/#Avoid-v-if-with-v-for-essential