Vuejs add and remove classes with v-for - javascript

i'm making a list of items with vuejs v-for loop. I have some API data from server.
items: [
{
foo: 'something',
number: 60
},
{
foo: 'anything',
number: 15
},
{
foo: 'text',
number: 20,
}
]
Template
<div v-for="(item,index) in items" :key="index">
<div :class="{ active: ????}" #click="toggleActive">
{{ item.foo }}
{{ item.number }}
</div>
</div>
JS
methods: {
toggleActive() {
//
}
}
I need following: When i'm clicking on div add class active, if i have already active class - remove active class.( toggle ). Also i can select multiple items.
How can i do this? I don't have boolean variable in items array, and i shouldn't move item in a separate component

Here you go.
new Vue({
el: "#app",
data: {
items: [{
foo: 'something',
number: 60
},
{
foo: 'anything',
number: 15
},
{
foo: 'text',
number: 20,
}
]
},
methods: {
toggleActive(index) {
let item = this.items[index];
item.active = !item.active;
this.$set(this.items, index, item);
}
}
})
.active {
color: red;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<div v-for="(item,index) in items" :key="index">
<div :class="{ active: item.active}" #click="toggleActive(index)">
{{ item.foo }} {{ item.number }}
</div>
</div>
</div>
Here's a JS Fiddle:
https://jsfiddle.net/eywraw8t/250008/

App.vue
<template>
<div>
<div
v-for="(item, i ) in items"
:key="i"
:class="{ active: i === activeItem}"
>
// some looped items from data here
// button for active toggle
<button #click="selectItem(i)"> make item active </button>
</div>
</div>
</template>
Data and Methods
<script>
export default {
data() {
return {
activeItem: null,
};
},
methods: {
selectItem(i) {
this.activeItem = i;
},
},
};
</script>

Related

How to use the WIZARD component in vue3 correctly

I am using the wizard component, which is a step by step, I am placing a select in each tab, but I have the problem that only the select of the first tab is shown, how could I solve this problem?
component:https://bahadirsofuoglu.github.io/form-wizard-vue3/guide/installation.html
I attach my code:
<template>
<div id="app">
<div>
<h1>Customize with Props</h1>
<Wizard
squared-tabs
card-background
navigable-tabs
scrollable-tabs
:nextButton="{
text: 'test',
icon: 'check',
hideIcon: true, // default false but selected for sample
hideText: false, // default false but selected for sample
}"
:custom-tabs="[
{
title: 'Step 1',
},
{
title: 'Step 2',
},
]"
:beforeChange="onTabBeforeChange"
#change="onChangeCurrentTab"
#complete:wizard="wizardCompleted"
>
<template v-if="currentTabIndex === 0">
<h5>Tab 0</h5>
<v-select
:items="options"
label="Select an option"
v-model="selectedOption"
></v-select>
</template>
<template v-if="currentTabIndex === 1">
<h5>Tab 1</h5>
<v-select
:items="options"
label="Select an option"
v-model="selectedOption"
></v-select>
</template>
</Wizard>
</div>
</div>
</template>
<script>
import "form-wizard-vue3/dist/form-wizard-vue3.css";
import Wizard from "form-wizard-vue3";
export default {
name: "App",
components: {
Wizard,
},
data() {
return {
currentTabIndex: 0,
options: ["Option 1", "Option 2", "Option 3"],
selectedOption: "",
};
},
methods: {
onChangeCurrentTab(index, oldIndex) {
console.log(index, oldIndex);
this.currentTabIndex = index;
},
onTabBeforeChange() {
if (this.currentTabIndex === 0) {
console.log("First Tab");
}
console.log("All Tabs");
},
wizardCompleted() {
console.log("Wizard Completed");
},
},
};
</script>
attached image:
tab 1:
tab2:
From quick look at the documentation it seems that you need all of the steps content to be wrapped with <template #activeStep>. And then inside your v-if logic probably.

Set checkbox selected in Vue

I am beginner in vue and web developing. I make my app with Laravel and Vue.
I have this code:
created: function () {
let self = this;
self.setActive('tab1');
axios.get(this.$apiAdress + '/api/tasks/create?token=' + localStorage.getItem("api_token"))
.then(function (response) {
self.documentDircionary = response.data.documentDircionary;
self.selectedDocumentDircionary = response.data.selectedDocumentDircionary;
}).catch(function (error) {
console.log(error);
self.$router.push({path: '/login'});
});
<template v-for="(option) in documentDircionary">
<div class="form-group form-row" :key="option.name">
<CCol sm="12">
<input type="checkbox" name="selectedDocuments[]" :id="option.value" /> {{ option.label }}
</CCol>
</div>
</template>
This code show me inputs - and it's work fine.
I have problem with set selected attribute for selected checkbox.
In array selectedDocumentDircionary results from api:
"selectedProducts": [1,2,43]
How can I set checked for only this checkbox, witch selectedProducts?
Please help me
You can use :checked attribute to marked the checkbox checked as per the selectedProducts you have.
Working Demo :
const app = new Vue({
el: '#app',
data() {
return {
selectedProducts: [1, 2, 43],
documentDircionary: [{
name: 'checkbox1',
value: 1,
label: 'Checkbox 1'
}, {
name: 'checkbox2',
value: 2,
label: 'Checkbox 2'
}, {
name: 'checkbox3',
value: 3,
label: 'Checkbox 3'
}, {
name: 'checkbox4',
value: 4,
label: 'Checkbox 4'
}, {
name: 'checkbox43',
value: 43,
label: 'Checkbox 43'
}]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.0/vue.js"></script>
<div id="app">
<template v-for="option in documentDircionary">
<div :key="option.name">
<input type="checkbox" name="selectedDocuments[]" :id="option.value" :checked="selectedProducts.includes(option.value)" /> {{ option.label }}
</div>
</template>
</div>
You can set :checked based on if the id of the current element is in the array:
<template v-for="(option) in documentDircionary">
<div class="form-group form-row" :key="option.name">
<CCol sm="12">
<input type="checkbox" name="selectedDocuments[]" :id="option.value" :checked="selectedProducts.includes(option.value)" /> {{ option.label }}
</CCol>
</div>
</template>

How can I automaticaly switch through VueJS tabs on an Interval

This is my very first VueJS Applicationa and it is more of a POC than a serious project.
Nevertheless I really nead a solution to my problem.
The Application resembles a dashboard for several "Groups" and each group has a certain amount of data that I can visualize. For each kind of data I have one tab and in every tab there is this kind of data visualized for each group.
So. Now the problem that im facing is that as a dashboard it should be able to display all the information without a user interaction. To realise that, I need the tabs to change on a timeinterval. so for example every 5 Minutes another Tab is shown. I hope that makes sense.
I googled some solutions already, but for them to work I would need to rewrite almost all of my current work. So I am looking for a solution that I can implement into my current project.
Here is my code so far:
Dashboard.vue
<template>
<div style="margin-left:220px" class="text-center m-3">
<tabs>
<tab title="Robots">
<div v-if="responseAvailable == true">
<div v-for="customer in result" class="customerCard">
<span class="card-content">
{{ customer.CustomerName }}
<hr />
<div v-for="robot in customer.ListOfRobots" :class="robot.State">
{{ robot.Name }}
</div>
</span>
</div>
</div>
</tab>
<tab title="Jobs" :selected="true">
<div v-if="responseAvailable == true">
<div v-for="customer in result" class="customerCard">
<span class="card-content">
{{ customer.CustomerName }}
<hr />
<apexchart width="500" type="bar"
:options="customer.options" :series="customer.ListForFrontend">
</apexchart>
</span>
</div>
</div>
</tab>
<tab title="Queues">
<div v-if="responseAvailable == true">
<div v-for="customer in result" class="customerCard">
<span class="card-content">
{{ customer.CustomerName }}
<hr />
<div v-for="queue in customer.ListOfQueues" id="chart">
{{ queue.Name }}
<apexchart type="line" height="200" :options="lineChartOptions" :series="queue.FrontedListQueues"></apexchart>
</div>
</span>
</div>
</div>
</tab>
<tab title="Triggers">
<div v-if="responseAvailable == true">
<div v-for="customer in result" class="customerCardPie">
<span class="card-content">
{{ customer.CustomerName }}
<hr />
<apexchart type="pie" width="280" :options="piechartOptions" :series="customer.TriggersFE"></apexchart>
</span>
</div>
</div>
</tab>
</tabs>
</div>
</template>
<script>
import M from 'materialize-css'
import ApexCharts from 'apexcharts'
import Tab from './Tabs/Tab.vue'
import Tabs from './Tabs/Tabs.vue'
export default {
name: 'Dashboard',
components: {
Tab,
Tabs
},
/*data() {
return {
result: null,
responseAvailable: null,
timer: ""
}
},*/
data: () => ({
result: null,
responseAvailable: null,
timer: "",
pieSeries: [34,345,123,6,75],
piechartOptions: {
chart: {
width: 280,
type: 'pie',
},
labels: ['Enabled', 'Disabled'],
colors: ['#11FF00', '#FF0000',],
responsive: [{
breakpoint: 480,
options: {
chart: {
width: 200
},
legend: {
position: 'bottom'
}
}
}]
},
lineChartOptions: {
chart: {
height: 200,
type: 'line',
zoom: {
enabled: false
}
},
dataLabels: {
enabled: false
},
colors: ['#11FF00', '#FFA200', '#FF0000', ],
stroke: {
curve: 'smooth'
},
grid: {
row: {
colors: ['#f3f3f3', 'transparent'], // takes an array which will be repeated on columns
opacity: 0.5
},
},
xaxis: {
//categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep'],
categories: ['01:00', '02:00', '03:00', '04:00', '05:00', '06:00', '07:00', '08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00', '21:00', '22:00', '23:00', '00:00',],
}
},
}),
created() {
this.fetchAPIData();
this.timer = setInterval(this.fetchAPIData, 900000)
},
mounted() {
M.AutoInit(); // That way, it is only initialized when the component is mounted
},
methods: {
fetchAPIData() {
this.responseAvailable = false;
fetch("https://localhost:44378/api/values", {
"method": "GET",
})
.then(response => {
if (response.ok) {
return response.json()
} else {
alert("Server returned " + response.status + " : " + response.statusText);
}
})
.then(response => {
this.result = response;
console.log(this.result[0].ListOfQueues[1].FrontedListQueues[0].data);
this.responseAvailable = true;
})
.catch(err => {
alert(err);
});
},
cancelAutoUpdate() {
clearInterval(this.timer)
}
},
beforeDestroy() {
clearInterval(this.timer)
}
};
</script>
Tab.vue
<template lang="html">
<div class='tab' v-show='isActive'>
<slot></slot>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: 'Tab'
},
name: { requred: true },
selected: { default: false }
},
data() {
return {
isActive: true
}
},
mounted() {
this.isActive = this.selected;
},
computed: {
href() {
return "#" + this.name.toLowerCase().replace(/ /g, '-');
}
}
}
</script>
Tabs.vue
<template lang="html">
<div>
<ul class='tabs__header'>
<li v-for='tab in tabs'
:key='tab.title'
#click='selectTab(index)'
:class='{"tab__selected": (index == selectedIndex)}'>
<a :href="tab.href" #click="selectNewTab(tab)">{{ tab.name }}</a>
</li>
</ul>
<slot></slot>
</div>
</template>
<script>
export default {
data() {
return {
selectedIndex: 0, // the index of the selected tab,
tabs: [] // all of the tabs
}
},
created() {
this.tabs = this.$children
},
mounted() {
//this.selectTab(0);
let tabsVue = this;
setInterval(function () {
tabsVue.changeToNextTab();
}, 1000);
},
methods: {
changeToNextTab() {
if (!this.tabs.length) {
return;
}
let activeIndex = this.tabs.findIndex(tab => tab.isActive);
if (activeIndex === -1) {
activeIndex = 0;
}
// add one to the index for the next tab
activeIndex++;
if (activeIndex >= this.tabs.length) {
// Reset to first tab if on the last tab
activeIndex = 0;
}
this.selectNewTab(this.tabs[activeIndex]);
},
selectNewTab(slectedTab) {
this.tabs.forEach(tab => {
tab.isActive = (tab.name == slectedTab.name)
})
}
}
}
</script>
Any help is much appreciated. And how I said, this is my first VueJS project so I am aware some of the things are not best practice.
Codesandbox: https://codesandbox.io/s/priceless-mountain-m9fl9?file=/src/main.js

Controlling the style of a label in a v-treeview in VuetifyJS

I have the following HTML
<div id="app">
<v-app id="inspire">
<v-treeview :items="items"></v-treeview>
</v-app>
</div>
and the following Javascript:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: [
{
id: 1,
name: 'Applications :',
children: [
{ id: 2, name: 'Calendar : app' },
],
},
{
id: 15,
name: 'Downloads :',
children: [
{ id: 16, name: 'October : pdf' },
],
}
],
}),
})
Let's say that I want to make the font-size of the Downloads label bigger or make it bold.
Is this possible with Vuetify and it's v-treeview? If so, how? There does not appear to be a way to control the styling of the labels.
Everything can be controlled in Vuetify. It's easier with some things and harder with others:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: [{
id: 1,
name: 'Applications :',
children: [{
id: 2,
name: 'Calendar : app'
}, ],
},
{
id: 15,
name: 'Downloads :',
children: [{
id: 16,
name: 'October : pdf'
}, ],
}
],
}),
})
.large {
font-size: 20px;
font-weight: 700;
}
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#5.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script>
<div id="app">
<v-app id="inspire">
<v-treeview :items="items">
<template v-slot:label="{ item }">
<span
:class="{ large: !!(item.id === 15) }"
>
{{ item.name }}
</span>
</template>
</v-treeview>
</v-app>
</div>
Look at the API description on Vuetify's component pages, and look for slots - if a component has the slot you look for, then you can work with it quite easily. If not, then you may have to tweak classes or even the components.
v-treeview's documentation: https://vuetifyjs.com/en/components/treeview/
VTreeview has a slot called label. You can "invoke" that slot by:
<v-treeview :items="items">
<template v-slot:label="{ item, leaf, selected, indeterminate, active, open }">
<!-- your code comes here --->
</template>
</v-treeview>
You can pass in item, leaf, selected, indetermintae, active or open props, that you can use in the scope of the <template></template>. In my snippet, we only needed the item, that's why I only passed that down.
My snippet only works, if you have an item with id: 15 - but you can put any logic there: calling methods, computed properties, etc.
For more information you can look in Vue scoped slots and destructuring slot props.
You must to use the slot label. Try something like this:
<v-treeview :items="items">
<template v-slot:label="{ item }">
<span style="font-weight: bold">{{ item.name }}</span>
</template>
</v-treeview>

VueJS - Dynamic repeating component

I have a <filter> component in a .vue file that has some properties to control the filter of a query.
This <filter> can be added / removed on-the-fly as the user needs. It's behavior is very similar to the Google Analytics segmentation filter, or Advanced Custom Fields from WordPress.
The only solution I see is instantiating this component dynamically and iterate over an array of these components inside my main app, but I don't exactly know how to do it.
Vue.component("my-filter", {
template: "#filterTemplate",
data: function() {
return {
field: null,
value: null
}
},
mounted: function() {
this.$emit("filter-created", this);
},
methods: {
removeFilter: function() {
console.log("Remove this filter");
}
}
});
var app = new Vue({
el: "#app",
data: {
filtersCount: 5,
filters: [] // PROBLEM! I can't decrement on my filtersCount and remove the correct filter. Iteration should be over my "filtersCount" property.
},
methods: {
filterCreated: function(filterObj) {
this.filters.push(filterObj);
},
addFilter: function() {
this.filtersCount += 1;
}
}
});
* {
font-family: "Helvetica", "mono";
font-size: 16px;
}
.filterContainer + .filterContainer {
margin-top: 10px;
}
.filterContainer {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
<!-- I shouldn't iterate over an integer value, but over an array of objects to remove the right ones -->
<my-filter v-on:filter-created="filterCreated" v-for="(index, filter) in filtersCount" :key="index"></my-filter>
<br>
<button #click="addFilter">Add filter</button>
</div>
<script type="text/x-template" id="filterTemplate">
<div class="filterContainer">
<input type="text" :value="field" placeholder="Field" />
<input type="text" :value="value" placeholder="Value" />
<button #click="removeFilter">Remove filter</button>
</div>
</script>
A few things can be changed to get it working (I'm just assuming what you are looking for!)
First, you don't need a data property for counting filters (filtersCount), you can loop through the filters property.
Second, adding this to the filters property can cause unexpected behaviour because this references the entire Vue component. I would recommend adding plain objects that represent the filter data and pass the data as props. Note: that the index is also passed as a prop which can be referenced and allow the filter to be removed through emitting
And lastly, your v-for seems to be reversed. It should be (filter, index) instead of (index, filter).
Vue.component("my-filter", {
template: "#filterTemplate",
props: [
'field',
'value', // filter data
'id',
'index' // index that allows this filter to be removed
],
data: function() {
return {
field: this.field,
value: this.value
}
},
methods: {
removeFilter: function() {
this.$emit('remove-filter', this.index);
},
handleInput: function(prop, e) {
this.$emit('update-filter', { index: this.index, prop, value: e.target.value });
}
}
});
window.vm = new Vue({
el: "#app",
data: {
filters: [
{ id: 1, field: null, value: null },
{ id: 2, field: null, value: null },
{ id: 3, field: null, value: null },
{ id: 4, field: null, value: null },
{ id: 5, field: null, value: null }
]
},
methods: {
addFilter: function() {
var id = Math.max.apply(Math,this.filters.map(function(o){return o.id;})) + 1;
this.filters.push({ id, field: null, value: null });
},
removeFilter: function(index) {
this.filters.splice(index, 1);
},
updateFilter: function(payload) {
this.filters[payload.index][payload.prop] = payload.value;
}
}
});
* {
font-family: "Helvetica", "mono";
font-size: 16px;
}
.filterContainer + .filterContainer {
margin-top: 10px;
}
.filterContainer {
display: block;
border: 1px solid black;
padding: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
<div id="app">
<button #click="addFilter">Add Filter</button>
<br><br>
<my-filter v-for="(filter, index) in filters" :key="index" :field="filter.field" :value="filter.value" :id="filter.id" :index="index" #remove-filter="removeFilter" #update-filter="updateFilter"></my-filter>
</div>
<script type="text/x-template" id="filterTemplate">
<div class="filterContainer">
<div>Index: {{ index }}, ID: {{ id }}</div>
<input type="text" :value="field" placeholder="Field" #input="handleInput('field', $event)" />
<input type="text" :value="value" placeholder="Value" #input="handleInput('value', $event)" />
<button #click="removeFilter">Remove filter</button>
</div>
</script>

Categories

Resources