Setting an active state with Vue JS and for loop - javascript

I have a list of strings in Vue, and I'm using v-for to list all of them in "list-group" with Bootstrap. I want to set the "active" state on click on one of the items, but I can't find a way to ID a specific item on the list with the for loop.
HTML -
<div class="list-group" id="ServersList">
{{Server.text}}
</div>
VueJS -
var ServersList = new Vue({
el: '#ServersList',
data: {
Servers: [
{ text: '1' },
{ text: '2' },
{ text: '3' },
{ text: '4' },
{ text: '5' },
{ text: '6' }
})

You can use v-for="(Server, index) in Servers". Index will store the current index for each element. With this you can then call a defined method like so:
In your viewmodel definition add this:
data: {
currentIndex: 0
},
methods: {
myClick(index) {
this.currentIndex = index
}
}
modify your a tag in your template:
<a href="#"
v-for="(server, index) in Servers"
:class="{yourActiveClass: currentIndex === index, yourInactiveClass: currentIndex !== index}"
#click="myClick(index)">

Related

Pug hasn't access to computed properties in vue inside dynamic class attribute

So, i tried to dynamically toggle className, based on computed property, but it looks like pug doesn't have access to computed properties. I tried to manually set true to a className, then it's working.
I tried to reassign computed property to pug variable, but it still doesn't work
When using pure html, classes dynamically toggle correctly
Pug:
main#app.container
- var isPinkDefinitely = isPink ? 'pink' : 'gray';
div.section(
v-bind:class=[
'custom-section',
{
'custom-section--pink': isPink
}
]
v-bind:style=[
{
'background-color': isPinkDefinitely
}
]
) {{ isPink }}
form(#submit.prevent="addItem")
label.label Add another
div.field.has-addons
div.control
input.input(required, autofocus, v-model="newItem", placeholder="Remake this in React")
button(type="submit").button.is-info
i.fa.fa-plus
span Add
transition(name="slide")
div(v-show="items.length > 0")
hr
ul
transition-group(name="slide")
li(v-for="(item, index) in items", :key="item.id")
button(#click="removeItem(index)").button.is-danger
i.fa.fa-trash
span(v-text="item.desc")
hr
span(v-text="'Total: ' + items.length")
JS:
new Vue({
el: '#app',
data: {
items: [
{id: 1, desc: "Lorem"},
{id: 2, desc: "Ipsum"},
{id: 3, desc: "Dolor"},
],
newItem: '',
},
computed: {
isPink() {
return true;
}
},
methods: {
addItem () {
const id = this.items.length + 1
this.items.push({id, desc: this.newItem})
this.newItem = ''
},
removeItem (index) {
this.items.splice(index, 1)
},
},
})
https://codepen.io/itprogressuz/pen/qBoePob?editors=1010
UPD:
SO the basically solution was to just write all classes in one line inside just an object. see solution of #aykut
I just tried to solve your problem and I think i successed. You could use variable like me. If you want change it in computed function, it will change dynamically. You could also change it in methods functions when get users events. Here, my solution.
main#app.container
div.section(
class="default-style"
:class="{'bg-pink': isPink }"
) {{ setIsPink }}
form(#submit.prevent="addItem")
label.label Add another
div.field.has-addons
div.control
input.input(required, autofocus, v-model="newItem", placeholder="Remake
this in React")
button(type="submit").button.is-info
i.fa.fa-plus
span Add
transition(name="slide")
div(v-show="items.length > 0")
hr
ul
transition-group(name="slide")
li(v-for="(item, index) in items", :key="item.id")
button(#click="removeItem(index)").button.is-danger
i.fa.fa-trash
span(v-text="item.desc")
hr
span(v-text="'Total: ' + items.length")
// css file
.default-style{
background-color: gray;
}
.bg-pink{
background-color: pink;
}
// js file
new Vue({
el: '#app',
data: {
isPink: false,
items: [
{id: 1, desc: "Lorem"},
{id: 2, desc: "Ipsum"},
{id: 3, desc: "Dolor"},
],
newItem: '',
},
computed: {
setIsPink() {
this.isPink = !this.isPink;
return this.isPink;
}
},
methods: {
addItem () {
const id = this.items.length + 1
this.items.push({id, desc: this.newItem})
this.newItem = ''
},
removeItem (index) {
this.items.splice(index, 1)
},
},
})

Loop through and delete elements in an array of objects

In my Vue.js project I have an array of objects which I want to list through and display in the browser.
My array contains four objects, I want to display only 3. The way I choose the 3 objects are dependent on a preference setting that the user has chosen somewhere else in the project and stored in a variable (below it is called userPreference). I am currently stuck on the best and most efficient way to remove one of the objects from my array based on the userPreference value.
My v-for in my template
<ul v-for="item in getOutroItems"><li>item<li></ul>
My object:
data() {
return {
outroItems: [{ title: "outro1", text: "XYZ" }, { title: "outro2", text: "ABC" }, { title: "outro3",
text`enter code here`: "QRS" }, { title: "outro4", text: "TUV" }],
userPreference: ""
};
}
My computed property (this is what I have so far)
getOutroItems() {
this.outroItems.filter((value) => {
if(this.userPreference === "newsletter") {
/// here I want to remove outro2 from my array and return an array with the other 3 values
} else (this.userPreference === "noNewsletter") {
/// here I want to remove outro3 from my array and return an array with the other 3 values
}
})
}
So, what is the best way to remove a specific element from an array?
Thanks in advance, and let me know if anything wasn't clear enough.
Your requirement can be fulfilled by below code as array.filter just wants true or false in its return to accept or remove an element from its array.
getOutroItems() {
this.outroItems.filter((value) => {
if(this.userPreference === "newsletter") {
// here I want to remove outro2 from my array and return an array with the other 3 values
return value.title != 'outro2';
} else (this.userPreference === "noNewsletter") {
// here I want to remove outro3 from my array and return an array with the other 3 values
return value.title != 'outro3';
}
})
}
However if you want to not create another array if it is big. you should go with swapping such elements to be removed with the end indexed element in the array and popping those many elements from the array.
There are multiple ways of getting the correct items from an array.
My preferred method and in your example: Using array.filter
const outroItems = [
{ title: "outro1", text: "XYZ" },
{ title: "outro2", text: "ABC" },
{ title: "outro3", text: "QRS" },
{ title: "outro4", text: "TUV" }
];
const leftOverItems = outroItems.filter((item) => item.title !== "outro2");
console.log(leftOverItems);
Another option is to find the index of the item to remove and then remove it with splice
const outroItems = [
{ title: "outro1", text: "XYZ" },
{ title: "outro2", text: "ABC" },
{ title: "outro3", text: "QRS" },
{ title: "outro4", text: "TUV" }
];
const itemToDelete = outroItems.find((item) => item.title === "outro2");
const indexToDelete = outroItems.indexOf(itemToDelete);
outroItems.splice(indexToDelete, 1);
console.log(outroItems);
Combining any of the functions above with a function will prevent you from writing duplicate code.
const itemToRemove = (arr, attr, name) => {
return arr.filter((item) => item[attr] !== name);
}
const outroItems = [
{ title: "outro1", text: "XYZ" },
{ title: "outro2", text: "ABC" },
{ title: "outro3", text: "QRS" },
{ title: "outro4", text: "TUV" }
];
// Remove from "outroItems" where "title" is "outro2"
const removed2 = itemToRemove(outroItems, "title", "outro2");
// Remove from "outroItems" where "title" is "outro3"
const removed3 = itemToRemove(outroItems, "title", "outro3");
// Remove from "outroItems" where "text" is "TUV"
const removedTUV = itemToRemove(outroItems, "text", "TUV");
console.log(removed2);
console.log(removed3);
console.log(removedTUV);

How to check whether a v-checkbox is selected in Vue JS?

I have a checkbox with the following events and props:
<v-checkbox
v-for="planets in allPlanets" :key="`planets_${planets.id}`"
:label="planets.name"
:value="planets.id"
v-model="selectedPlanets"
/>
Given that all of the checkboxes are brought in using a v-for, how can I check whether a checkbox is selected using a method or mounted function in Vue JS?
For example:
methods: {
checkSelected() {
????
},
Add a planets.selected key.
allPlanets: [
{ name: 'Planet name', value: 'Planet value' , selected : false },
...,
...
],
}
And in your template:
<v-checkbox
v-for="planets in allPlanets" :key="`planets_${planets.id}`"
:label="planets.name"
:value="planets.id"
v-model="planets.selected"
/>
Similar to:
Display multiple checkbox in table format using <v-for> and <v-checkbox> in Vuetify?
you have to make such a structure, so you know for each id, whether it is checked or not
new Vue({
data: () => ({
allPlanets: [
{
id: 32,
name: "planent",
selected: false
},
{
id: 365,
name: "planet 2",
selected: false
}
],
}),
methods: {
checkSelectedByIndex(index) {
return this.allPlanets[index].selected
},
checkSelectedById(id) {
return this.allPlanets.find(p => p.id === id)?.selected ?? false
}
}
});
and in the you have to set the v-model="planets.selected"
Given your layout the simplest methods is:
methods: {
checkSelected(id) {
return this.selectedPlanets.includes(id)
},
}

Data not being passed to Vue component

I have a Vue component receiving an array of 'items' from its parent.
I've sorted them into categories, two 'items' in each category:
computed: {
// sort items into categories
glass: function() {
return this.items.filter(i => i.category === "glass").slice(0, 2);
},
ceramics:
// etc...
I need to place both items in categories.items to then pass them as props to another component:
data() {
return {
categories: [
{ name: "Glass", sort: "glass", items: {} },
{ name: "Ceramics", sort: "ceramics", items: {} },
{ name: "Brass", sort: "brass", items: {} },
{ name: "Books/Comics", sort: "books", items: {} },
{ name: "Collectibles", sort: "collectibles", items: {} },
{ name: "Pictures", sort: "pictures", items: {} },
{ name: "Other", sort: "other", items: {} }
]
};
},
When I use created or mounted nothing is passed through, when I use beforeDestroy or destroy and console.log the results it works fine, but, they're of no use when exiting the page.
The 'items' are from an Axios GET request, could this be why?
GET request from parent component:
methods: {
fetchItems() {
// items request
let uri = "http://localhost:8000/api/items";
this.axios.get(uri).then(response => {
// randomize response
for (let i = response.data.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[response.data[i], response.data[j]] = [
response.data[j],
response.data[i]
];
}
this.items = response.data;
});
}
},
Passing props to child component:
<div
class="items-container"
v-for="category in categories"
:key="category.name"
>
<router-link :to="'category.link'" class="category-names-home-link">
<h2 class="category-names-home">{{ category.name }}</h2>
</router-link>
<router-link
:to="'category.link'"
class="home-view-all"
#mouseover.native="expand"
#mouseout.native="revert"
>View All...</router-link
>
<div class="items">
<!-- Pass to child component as props: -->
<SubItem :item="categories.items" />
<SubItem :item="categories.items" />
</div>
</div>
Don't bother adding the items to the categories, keep them separate
Instead of multiple computeds, use one computed object hash to store all the filtered sets:
computed: {
filtered() {
if (!this.items) return null;
const filtered = {};
this.items.forEach(item => {
if (filtered[item.category]) {
filtered[item.category].push(item);
} else {
filtered[item.category] = [item];
}
});
return filtered;
}
}
Result:
{
'Glass': [ ... ],
'Ceramic': [ ... ]
...
}
In the template:
<div>
<div v-for="category in categories" :key="category.name">
<div class="items" v-for="item in filtered[category.name]">
<SubItem :item="item" />
</div>
</div>
</div>
You can use a v-if in the parent to prevent displaying anything until the data is loaded:
<display v-if="items" :items="items"></display>
Here is a demo

How to traverse a list of items in VueJS

Here is my vue instance:
new Vue({
el: '#app',
data: {
showPerson: true,
persons:
[
{id: 1, name: 'Alex'},
{id: 2, name: 'Bob'},
{id: 3, name: 'Chris'}
],
},
methods: {
nextPerson: function(){
this.showPerson = false;
}
}
});
I am trying to walk the persons array of objects. I want the list to start with the first element of the array and below it should be a button which is responsible for hiding the previous element and showing the next element of the array. Once the user reaches the last element, the Next button should not go back to the first element.
Here is the HTML:
<div id="app">
<ul v-for="person in persons">
<li v-if="showPerson">{{person.name}}</li>
</ul>
<button #click="nextPerson">Next Person</button>
</div>
And the JSBin Link. At this moment I can only show and hide the items all at once and not one at a time. How can I implement this?
One of the ways of doing so would be to keep an index for the person being shown on screen. I've named this variable as shownPersonIndex.
Then, you need to show the next person on click of button. So in the click event handler, you need to increment the index by 1. Also, you need to ensure that the index value does not exceed the length of the array. So I've modified the click handler as follows:
nextPerson: function() {
if(this.shownPersonIndex < (this.persons.length - 1)) {
this.shownPersonIndex++;
}
}
Finally, you can either use a computed to display the currently shown person or an inline expression like this.persons[this.shownPersonIndex].name to show the person on screen.
I am using v-if="this.shownPersonIndex != this.persons.length - 1" to hide the "next" button as you reach the last element on the array.
new Vue({
el: '#app',
data: {
shownPersonIndex: 0,
persons: [{
id: 1,
name: 'Alex'
},
{
id: 2,
name: 'Bob'
},
{
id: 3,
name: 'Chris'
}
],
},
methods: {
nextPerson: function() {
if(this.shownPersonIndex < (this.persons.length - 1)) {
this.shownPersonIndex++;
}
}
},
computed: {
shownPerson: function() {
return this.persons[this.shownPersonIndex];
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<div id="app">
Person: {{ shownPerson.name }}
<button v-if="this.shownPersonIndex != this.persons.length - 1" #click="nextPerson">Next Person</button>
</div>

Categories

Resources