How can I automaticaly switch through VueJS tabs on an Interval - javascript

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

Related

Change element prop in runtime

I have a chart component, and my job is to make a button to change it's type (eg. columns to pie), but i don't know how to change it on a button click event. Here's the structure of the component (the idea is to change the :series-defaults-type when the button with ChangeType id is pressed)
<template>
<div style="width: 100%;overflow: overlay;border-radius: 20px;">
<button id="changeType" #click="changeType()">Change</button>
<chart-more-option :kpiName="'EquipmentRetirementForecast'" v-if="showMoreOptions"/>
<chart :title-text="'Equipment Retirement Forecast'"
:title-color="'#FFF'"
:title-font="'openSans'"
:chart-area-background="'#1B1534'"
:legend-visible="false"
:series-defaults-type= "'column'"
:series="series"
:category-axis="categoryAxis"
:axis-defaults-color="'#FFF'"
:axis-defaults-labels-rotation-angle="'30'"
:value-axis="valueAxis"
:tooltip="tooltip"
:theme="'sass'"
:zoomable-mousewheel="true">
</chart>
</div>
</template>
<script>
import { Chart } from '#progress/kendo-charts-vue-wrapper';
import ChartMoreOption from '../ChartMoreOption';
export default {
name: 'EquipmentRetirementForecast',
components: {
'chart': Chart,
ChartMoreOption
},
props: {
fetchData: {
type: Boolean,
default: false
},
showMoreOptions: {
type: Boolean,
default: true,
},
},
watch: {
labelAlign(){
var c = this.$refs.chart
c.updateWidget();
}
},
computed:{
requestBody(){
return this.$store.getters['usersession/getTopologyRequestBody']
},
series(){
return this.$store.getters['riskmanagement/getRetirementForecastSeries']
},
categoryAxis(){
return this.$store.getters['riskmanagement/getRetirementForecastCategoryAxis']
},
},
data: function() {
return {
valueAxis: [{
line: {
visible: false
},
minorGridLines: {
visible: true
},
labels: {
rotation: "auto"
}
}],
tooltip: {
visible: true,
template: "#= series.name #: #= value #",
},
}
},
mounted(){
if(this.fetchData){
this.$store.dispatch("riskmanagement/FetchRetirementForecastData",this.requestBody).then(()=>{
});
}
},
methods: {
changeType(){
//code goes here
}
}
}
</script>
<style src="../style-dashboard.scss" lang="scss" scoped />
This is the chart i need to change:
Changing the :series-defaults-type to pie by hand, it works, but i need to make that change in a button click, as follows:
Add a data property and give it the default of 'column', name it for example chartType. Then inside the changeType() you add this.chartType = 'pie'. And change :series-defaults-type= "'column'" to :series-defaults-type= "chartType".
Also remember to NOT use : for attribute values that are hardcoded. So :chart-area-background="'#1B1534'" should be chart-area-background="#1B1534".

how to display image into vue js Datatable

guys I need to display an image into the field of Datatable I've tried everything but nothing works,
at that moment my code is:
<q-table
:data="driverTable.data"
:columns="driverTable.columns"
row-key="objectId"
>
<template v-slot:no-data="{ icon, message, filter }">
<div class="full-width row flex-center q-gutter-sm">
<q-icon size="2em" name="sentiment_dissatisfied" />
<span>{{ message }}</span>
<q-icon size="2em" :name="filter ? 'filter_b_and_w' : icon" />
</div>
</template>
<template v-slot:top="props">
<q-btn flat round dense
#click="props.toggleFullscreen" color="grey-8" class="q-ml-md"
:icon="props.inFullscreen ? 'fullscreen_exit' : 'fullscreen'"
:v-bind="props.inFullscreen"
:title="props.inFullscreen ? 'Minimiser' : 'Plain ecran'"
/>
</template>
</q-table>
and my js code:
data () {
return {
driverTable: {
columns: [
{
name: 'objectId',
field: row => row.objectId,
format: val => ''
},
{
name: 'name',
label: 'Nom complete',
align: 'left',
field: row => row.name,
format: val => `${val}`,
sortable: true,
classes: 'ellipsis'
},
{
name: 'carPermit',
required: true,
label: 'Permit',
field: row => row.carPermit, //
align: 'center',
format: val => `${'<img height="75%" width="75%" src="' + val + '"/>'}`
}
],
data: []
}
}
}
I've tried <img> into field format and bild on it the url source but it gives me just the balise on stiring format
please follow this
<q-table
:data="driverTable.data"
:columns="driverTable.columns"
row-key="objectId"
>
<template v-slot:no-data="{ icon, message, filter }">
<div class="full-width row flex-center q-gutter-sm">
<q-icon size="2em" name="sentiment_dissatisfied" />
<span>{{ message }}</span>
<q-icon size="2em" :name="filter ? 'filter_b_and_w' : icon" />
</div>
</template>
<template v-slot:top="props">
<q-btn flat round dense
#click="props.toggleFullscreen" color="grey-8" class="q-ml-md"
:icon="props.inFullscreen ? 'fullscreen_exit' : 'fullscreen'"
:v-bind="props.inFullscreen"
:title="props.inFullscreen ? 'Minimiser' : 'Plain ecran'"
/>
</template>
<template v-slot:body-cell-carPermit="props">
<q-avatar>
<img :src="props.row.carPermit">
</q-avatar>
</template>
</q-table>
and remove format and field keys from cardPermit
data () {
return {
driverTable: {
columns: [
{
name: 'objectId',
field: row => row.objectId,
format: val => ''
},
{
name: 'name',
label: 'Nom complete',
align: 'left',
field: row => row.name,
format: val => `${val}`,
sortable: true,
classes: 'ellipsis'
},
{
name: 'carPermit',
required: true,
label: 'Permit',
align: 'center',
}
],
data: []
}
}
}

Vue.js: How to do validation in dynamic v-for

I get a JSON with 60 questions that are displayed every 10 out of 10. But I need to check that all of them have been answered before moving on to the next 10 questions.
My component:
<div class="test-questions comp" id="scrollb">
<div class="question" v-for="(q, index) in currentPageItems" :key="q.id">
<div class="statement">
{{q.id}}. {{q.text}}
</div>
<div class="options yes-no">
<div :class="{'active':relato.quest['q'+q.id] === 1}" class="caption option yes" #click="handleYes('q'+q.id, q.ck)">Sim</div>
<div :class="{'active':relato.quest['q'+q.id] === 0}" class="caption option no" #click="handleNo('q'+q.id, q.ck)">Não</div>
</div>
</div>
</div>
<div class="action-row">
<button v-if="pageNumber <= 4" class="btn btn-green" #click="next()"><span>Próximo</span></button>
<button v-if="pageNumber == 5" class="btn btn-green" #click="salvarRelato()"><span>Publicar</span></button>
</div>
My Script:
<script>
export default {
props: ['grupoid','getperguntas','perguntas'],
data(){
return {
perpage: 10,
pageNumber: 0,
relato: {
loading: false,
errors: '',
titulo: '',
sonho: '',
resumo: '0',
ck: {
c1: '',
c2: '',
c3: '',
c4: '',
c5: '',
c6: '',
c7: '',
exist: '',
},
quest: {
q1: '',q2: '',q3: '',q4: '',q5: '',q6: '',q7: '',q8: '',q9: '',q10: '',
q11: '',q12: '',q13: '',q14: '',q15: '',q16: '',q17: '',q18: '',q19: '',q20: '',
q21: '',q22: '',q23: '',q24: '',q25: '',q26: '',q27: '',q28: '',q29: '',q30: '',
q31: '',q32: '',q33: '',q34: '',q35: '',q36: '',q37: '',q38: '',q39: '',q40: '',
q41: '',q42: '',q43: '',q44: '',q45: '',q46: '',q47: '',q48: '',q49: '',q50: '',
q51: '',q52: '',q53: '',q54: '',q55: '',q56: '',q57: '',q58: '',q59: '',q60: '',
},
ckQuestAssociations: {
c1: [],
c2: [],
c3: [],
c4: [],
c5: [],
c6: [],
c7: [],
exist: [],
} ,
},
}
},
computed:{
currentPageItems(){
return this.perguntas['questions'].slice(this.pageNumber*this.perpage,this.pageNumber*this.perpage+this.perpage)
}
},
methods: {
next(){
this.pageNumber++;
document.getElementById("scrollb").scrollIntoView({behavior: "smooth"});
},
previous(){
this.pageNumber--;
},
handleYes(quest, ck) {
this.relato.quest[quest] = 1;
ck.forEach(c => {
if (this.relato.ckQuestAssociations[c].indexOf(quest) === -1) {
this.relato.ckQuestAssociations[c].push(quest);
++this.relato.ck[c];
}
});
},
handleNo(quest, ck) {
this.relato.quest[quest] = 0;
console.log(this.relato.quest);
console.log(this.relato.ck);
ck.forEach(c => {
let index = this.relato.ckQuestAssociations[c].indexOf(quest);
if (index !== -1) {
this.relato.ckQuestAssociations[c].splice(index, 1);
--this.relato.ck[c];
}
});
}
}
}
</script>
What would be the suggestion to resolve this?
                                                                                                                                                                                       
In next method check if every qi (0<i=<60) has value equals to 1 or 0 until the current page :
next(){
let valid=Object.values(this.relato.quest).slice(0,(pageNumber+1)*perpage).every(q=>q==0 || q==1)
if(valid){
this.pageNumber++;
document.getElementById("scrollb").scrollIntoView({behavior: "smooth"});
}
},

Vuejs add and remove classes with v-for

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>

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