Automatically Sliding Carousel - vue.js

I have a carousel in vue which has left and right arrows. So the carousel is changing when you click one of them. What I am trying to achieve is to change the carousel automatically after 5 seconds for example.
So I have a carousel component:
<template>
<div class="carousel" #keydown="checkSlide($event)" tabindex="0">
<slot></slot>
<button #click.prevent="next" class="btn btn-next"><i class="fa fa-angle-right"></i></button>
<button #click.prevent="prev" class="btn btn-prev"><i class="fa fa-angle-left"></i></button>
</div>
</template>
<script>
export default {
data () {
return {
index : 0,
slides : [],
slideDirection: '',
}
},
computed: {
slidesLength() {
return this.slides.length;
}
},
mounted(){
this.slides = this.$children;
this.slides.map( (slide,index) => {
slide.index = index;
});
},
methods: {
next(){
this.index++;
if(this.index >= this.slidesLength){
this.index = 0;
}
this.slideDirection = 'slide-right';
},
prev(){
this.index--;
if(this.index < 0){
this.index = this.slidesLength - 1;
}
this.slideDirection = 'slide-left';
},
checkSlide(event){
if(event.keyCode === 39){
this.next();
}else if (event.keyCode === 37){
this.prev();
}else {
return;
}
},
}
}
</script>
And I also have carouselslide component:
<template>
<transition :name="dir">
<div v-show="visible">
<slot></slot>
</div>
</transition>
</template>
<script>
export default {
data() {
return {
index : 0,
}
},
computed : {
visible() {
return this.index === this.$parent.index;
},
dir() {
return this.$parent.slideDirection;
},
}
}
</script>
At the end I am calling my carousel in my home page like this:
<carousel >
<carousel-slide v-for="ticket in tickets" :key="ticket.id" class="carousel-slider">
<img :src="ticket.src" :alt="ticket.name">
</carousel-slide>
</carousel>
Hope my question is clear and thanks for your helps...

You could use setInterval() to call next() function. After that you should add a beforeDestroy() method to clear the interval.
You can also add a button to to start/stop the interval with the same code.
Example to start auto-slide on page mounted()
mounted(){
....
this.interval = setInterval(() => { this.next() }, 5000)
},
beforeDestroy () {
clearInterval(this.interval)
}

I think we should have a method that do this with a setInterval stored so we can clear it when we call next or prev
data() {
return {
...
interval: null,
}
},
methods: {
switchInterval() {
// if it's already set we clear so we reset calculation
if (this.interval !== null) clearinterval(this.interval);
this.interval = setInterval(() => {
// call next or prev logic
}, 5000)
},
next() {
...
// you can use the same logic in prev()
this.switchInterval()
},
...
},
beforeDestroy() {
// just make sure this.interval is not null
if (this.interval) clearInterval(this.interval)
}

Related

Toggle button component how to reset from parent

There is a toggle component, which I connect to the parent. Found a bug, haven't found a solution yet.
toggle-button
<template>
<label :for='id + "_button"' :class='{"active": isActive}' class='toggle__button'>
<input type='checkbox' :id='id + "_button"' v-model='checkedValue'>
<span class='toggle__switch'></span>
</label>
</template>
<script>
export default ({
props: {
defaultState: {
type: Boolean,
default: false
},
id: {
type: String,
default: 'primary'
}
},
data() {
return {
currentState: this.defaultState
};
},
computed: {
isActive() {
return this.currentState;
},
checkedValue: {
get() {
return this.defaultState;
},
set(newValue) {
this.currentState = newValue;
this.$emit('change', newValue);
}
}
},
methods: {
reset() {
this.currentState = false ;
}
}
});
</script>
how can i succinctly make a reset button? I use this option now, but after reset, when I click on toggle, it does not work the first time.
<button
#click='
$refs.toggleOriginal.reset($event),
$refs.toggleAnalog.reset($event),
$refs.toggleAvailable.reset($event)
'
>
reset
</button>
Each toggle has ref in parent.
I think what you may be saying is that you have a toggle button that can flip a value anywhere (perhaps stored in a higher state?) and a reset button that can set the value back to it's initial default.
Keep the management of this data property handled outside of toggle, and make toggle responsible for simply flipping the value, and reset responsible for resetting it.
Here is what I would do:
// parent.vue
<template>
<div>
Toggled value: {{toggleValue}}
<resetButton v-model="toggleValue" />
<toggleButton v-model="toggleValue" />
</div>
</template>
<script>
export default {
data() {
return {
toggleValue: false,
}
}
}
</script>
// toggleButton.vue
<template>
<button #click="toggle">Toggle</button>
</template>
<script>
export default {
props: {
value: {
type: Boolean,
}
},
computed: {
toggleValue: {
get() {
return this.value;
},
set(val) {
this.$emit('input', val);
}
}
},
methods: {
toggle() {
this.toggleValue = !this.toggleValue
}
}
}
</script>
// resetButton.vue
<template>
<button #click="reset">Reset</button>
</template>
<script>
export default {
props: {
value: {
type: Boolean,
}
},
methods: {
reset() {
this.$emit('input', false);
}
}
}
</script>

Vue js: set notification count to zero

in my Vue Js code below, i created count for notification socket, so whenever a new notification received it count+ and appears beside the icon ... now i wanted to set back the count to zero when a user click on notification icon but i couldn't figure out how to do it.
below in app.vue i set a prop called number and i pass it to the sidebar to appear on all project pages , this number is the count of the notifications .. is there a way to set it back to zero after user click on the icon in the side bar?
<template>
<router-view :number="count" #send="getNewCount">
<sidebar :number="count" />
</router-view>
</template>
<script>
import sidebar from "#/views/Layout/DashboardLayout.vue";
import axios from "axios";
import {
io
} from "socket.io-client";
let socket = io("h***********/");
export default {
components: {
sidebar,
},
data() {
return {
user2: JSON.parse(sessionStorage.getItem("user")),
count: 0,
today: null,
};
},
props: {
number: {
type: Number,
default: 0,
},},
async created() {
console.log(this.count);
const response = await axios.get(
`https://l*********rs/api/${this.user2._id}/`, {
headers: {
Authorization: "Bearer " + sessionStorage.getItem("user"),
},
}
);
// await this.$store.dispatch("user", response.data.response);
socket.emit("user_connected", this.user2.username);
// console.log(this.user2,"userr")
socket.on("verify_connection", (data) => {
this.verify_connection = data;
console.log(data, "s")
});
socket.emit("user_connected", this.user2.username);
socket.on("verify_connection", (data) => {
console.log("heyy", data);
this.verify_connection = data;
});
socket.on("updated_flat", (data) => {
console.log("heyy", data);
this.makeToast(" success ", "'b-toaster-top-center");
});
socket.on("test", (data) => {
console.log("heyy", data);
// this.makeToast("success", "b-toaster-top-right");
});
;
},
methods: {
// playSound() {
// var audio = document.getElementById("audio");
// audio.play();
// },
getNewCount(data) {
this.count = data;
},
makeToast(variant = null, toaster, append = false) {
this.$bvToast.toast(" edited ", {
title: "BootstrapVue Toast",
variant: variant,
autoHideDelay: 10000,
toaster: toaster,
position: "top-right",
appendToast: append,
});
// this.playSound();
this.count = this.count + 1;
console.log(this.count,"count");
},
}
}
</script>
sidebar:
<template>
<div class="wrapper">
<side-bar >
<template slot="links">
<sidebar-item v-if="roles ==='Admin'"
:link="{
name: ' notifications',
path: '/notifications',
icon: 'ni ni-bell-55 text-green'
}">
</sidebar-item>
<p class="notifCount" v-if="roles ==='Admin'"> {{ number }} </p>
</template>
</side-bar>
<div class="main-content">
<dashboard-navbar :type="$route.meta.navbarType"></dashboard-navbar>
<div #click="$sidebar.displaySidebar(false)">
<fade-transition :duration="200" origin="center top" mode="out-in">
<!-- your content here -->
<router-view></router-view>
</fade-transition>
</div>
<content-footer v-if="!$route.meta.hideFooter"></content-footer>
</div>
</div>
</template>
<script>
/* eslint-disable no-new */
import PerfectScrollbar from 'perfect-scrollbar';
import 'perfect-scrollbar/css/perfect-scrollbar.css';
function hasElement(className) {
return document.getElementsByClassName(className).length > 0;
}
function initScrollbar(className) {
if (hasElement(className)) {
new PerfectScrollbar(`.${className}`);
} else {
// try to init it later in case this component is loaded async
setTimeout(() => {
initScrollbar(className);
}, 100);
}
}
import DashboardNavbar from './DashboardNavbar.vue';
import ContentFooter from './ContentFooter.vue';
import DashboardContent from './Content.vue';
import { FadeTransition } from 'vue2-transitions';
export default {
components: {
DashboardNavbar,
ContentFooter,
DashboardContent,
FadeTransition
},
props: {
number: {
type: Number,
default: 0,
},
},
methods: {
initScrollbar() {
let isWindows = navigator.platform.startsWith('Win');
if (isWindows) {
initScrollbar('sidenav');
}
},
},
computed: {
roles() {
let roles = JSON.parse(sessionStorage.getItem('user')).role;
return roles;
},},
mounted() {
this.initScrollbar()
}
};
</script>
Emit an event from sidebar and handle the event in the main app.
main app:
<template>
<!-- ... -->
<sidebar :number="count" #reset="onReset" />
<!-- ... -->
</template>
<script>
export default {
// ...
methods: {
onReset() {
// api calls, etc.
this.count = 0;
}
}
}
</script>
sidebar:
<template>
<!-- ... -->
<button #click="$emit('reset')">Reset notification count</button>
<!-- ... -->
</template>

Error when attempting to disable Navigation Buttons in VueJs

In my ticket processing application I currently have a back and forward button contained in my TicketRunner.vue Component, I would like to change it so that these buttons only appear if I have an associated case file, for which I've used V-If:
TicketRunner.Vue
<div class="level nav-btns" v-if='!currentTicketCaseFiles.length'>
<div class="buttons has-addons level-left">
<b-button
#click.prevent="navGoPrev()"
:disabled="currentStepIndex === 0 || navWaiting"
size="is-medium"
>
</div>
export default {
name: 'TicketRunner',
mixins: [NavStepsByIndexMixin()],
components: {
StagePresenter,
CaseFilesStage,
ParticipantsStage,
AttachmentsStage,
CaseFilesRunner,
TicketContextButtons,
},
data: function() {
return {
firstComponentsInitialization: true,
loadingConfirm: false,
confirmationModalActive: false,
confirmationSucceeded: undefined
}
},
props: {
ticketId: {
type: Number,
required: true,
},
},
provide() {
return {
contextButtons: {
capture: (name, callback, title) => this.$refs['contextButtons'].captureButton(name, callback, title),
release: (name) => this.$refs['contextButtons'].releaseButton(name),
enable: (name) => this.$refs['contextButtons'].enableButton(name),
disable: (name) => this.$refs['contextButtons'].disableButton(name),
},
};
},
computed: {
...mapGetters(['currentTicket', 'ticketCaseFiles', 'allCurrentTicketAttachments', 'currentTicketCaseFileNotAssociated',
'currentRequesterType', 'currentTicketStage', 'lastCaseFile']),
caseFiles() {
return this.ticketCaseFiles(this.ticketId);
},
ticketHasAttachments() {
return this.allCurrentTicketAttachments.length > 0;
},
isTicketAssociatedWithCaseFile() {
return !this.currentTicketCaseFileNotAssociated;
},
isFirstNavInitializationInProgress() {
return !this.navReady && this.firstComponentsInitialization;
},
isShowAttachmentsStep() {
return this.ticketHasAttachments && this.currentRequesterType !== 'unknown' &&
(this.isFirstNavInitializationInProgress || this.isTicketAssociatedWithCaseFile)
},
isCurrentTicketResolved() {
return this.currentTicket.status === 'resolved';
},
islastStep() {
return this.navLastStep() && this.lastCaseFile;
}
},
watch: {
ticketId(){
this.navigator.reset();
},
navReady() {
this.moveForwardIfReady();
this.firstComponentsInitialization = false;
}
},
methods: {
...mapActions(['confirmTicket']),
moveForwardIfReady() {
if (this.navigator.currentIndex === 0 && this.firstComponentsInitialization) {
let steps = 0
const step_names = ['case_files_stage']
for(const [_idx, name] of step_names.entries()) {
const ref_name = `step[${name}]`;
if (this.$refs.hasOwnProperty(ref_name) && this.$refs[ref_name].navReady) {
steps += 1
} else {
break
}
}
this.navigator.currentIndex += steps
}
},
confirm() {
this.$buefy.dialog.confirm({
message: this.t('tickets.stages.confirmation.simplified_confirm_reply'),
onConfirm: () => this.confirmStep()
})
},
async confirmStep() {
this.loadingConfirm = true;
const promise = this.confirmTicket(this.ticketId);
return promise.then((response) => {
this.confirmationModalActive = true;
this.confirmationSucceeded = true;
return true; // true is correct here. for goNext it makes parent to stay on on the current step
}).catch(() => {
this.confirmationModalActive = true;
this.confirmationSucceeded = false;
return true; // true is correct here. for goNext it makes parent to stay on on the current step
}).finally(() => this.loadingConfirm = false);
},
},
};
I then receive the following Console Error:
[Vue warn]: Property or method "currentTicketCaseFiles" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
I know that "!currentTicketCaseFiles.length" works successfully in the Component CaseFilesStage.vue, which makes me believe I should somehow connect the two? But importing it doesn't seem right to me either. I'm not quite sure how to tackle this issue as I'm quite new at VueJS, and would be happy for any pointers. I'll attach the CaseFilesStage.vue Component below.
CaseFilesStage.vue
<template>
<div class="hero">
<div class="block">
<template v-if="!currentTicket.spamTicket">
<b-field>
<b-input
v-model="filter"
:loading="loading"
:placeholder="t('tickets.stages.case_files.search.tooltip')"
v-on:keyup.enter.native="searchCaseFiles"
type="search"
icon="search"
:class="{ 'preview-enabled': showAttachmentsPreview}"
/>
</b-field>
<template v-if="foundCaseFiles.length">
<h4 class="title is-4 table-title">{{ t('tickets.stages.case_files.search.table_title') }}</h4>
<CaseFilesSearchTable
:case-files="foundCaseFilxes"
:found-by-data-points="foundCaseFilesByParticipant"
:show-header="true"
v-slot="cf">
<b-checkbox v-if="cfBelongsToCurrentTicket(cf.id)" :disabled="true" :value="true"></b-checkbox>
<b-checkbox v-else #input="onFoundCaseFile(cf.id, $event)"></b-checkbox>
</CaseFilesSearchTable>
</template>
<div v-else-if="lookupStatus === 'notFound'">
{{ t('tickets.stages.case_files.search.not_found') }}
<!-- display button here if above is activated -->
</div>
</template>
</div>
<template v-if='currentTicketCaseFiles.length'>
<h4 class="title is-4 table-title">{{ t('tickets.stages.case_files.table_title') }}</h4>
<CaseFilesTable :case-files="currentTicketCaseFiles" :show-header="true" v-slot="cf">
<DeleteButton
:model-id="cf.id"
modelName="CaseFile" >
</DeleteButton>
</CaseFilesTable>
</template>
</div>
</template>
<script>
import CaseFilesTable from '../tables/CaseFilesTable';
import CaseFilesSearchTable from '../tables/CaseFilesSearchTable';
import DeleteButton from '../../../../shared/components/controls/DeleteButton';
import { mapGetters, mapActions } from 'vuex';
import { mapServerActions } from "../../../../../../_frontend_infrastructure/javascript/lib/crudvuex_new";
export default {
name: 'CaseFilesStage',
data() {
return {
lookupStatus: 'waitingInput',
filter: '',
waiting: {},
foundCaseFiles: [],
foundCaseFilesByParticipant: {}
};
},
components: {
CaseFilesTable,
CaseFilesSearchTable,
DeleteButton
},
computed: {
...mapGetters(
['currentTicketCaseFiles', 'currentTicketCaseFileNotAssociated', 'currentTicket', 'showAttachmentsPreview']
),
loading() {
return this.lookupStatus === 'waitingServer';
},
allCaseFilesMix(){
this.currentTicketCaseFiles + this.foundCaseFiles
},
foundCaseFilesEx() {
return this.foundCaseFiles.filter((x) => !this.cfBelongsToCurrentTicket(x.id))
},
checkboxValue() {
if(!this.currentTicketCaseFileNotAssociated) {
return null;
}
return true;
},
navReady() {
return this.currentTicket.spamTicket || this.currentTicketCaseFiles.length > 0 || this.checkboxValue;
},
markSpam: {
get: function() {
return this.currentTicket.spamTicket
},
set: function(val) {
return this.updateTicket([this.currentTicket.id, { spam_ticket: val }]);
},
}
},
methods: {
...mapActions(['updateTicket']),
...mapServerActions(['createCaseFile', 'deleteCaseFile']),
cfBelongsToCurrentTicket(id){
return this.currentTicketCaseFiles.map((x) => x.caseFileId).includes(id);
},
cantAssignCaseFileCheckbox(isChecked){
if(isChecked) {
this.createCaseFile({ isCfNotAssociated: true });
} else {
this.deleteCaseFile(this.currentTicketCaseFileNotAssociated);
}
},
onFoundCaseFile(id, useIt){
console.log("onFoundCaseFile: ", id, useIt);
if(useIt) {
this.createCaseFile({ caseFileId: id });
} else {
this.deleteCaseFile(this.currentTicketCaseFiles.find({ caseFileId: id }));
}
},
searchCaseFiles() {
const newData = this.filter;
if (newData.length < 3) { // TODO: some smarter condition here
this.foundCaseFiles = [];
this.lookupStatus = 'waitingInput';
return;
}
this.lookupStatus = 'waitingServer';
this.$axios.get('case_files', { params: { "case_files.filter" : newData } })
.then((response) => {
this.foundCaseFiles = response.data.caseFilesSearchResult.caseFiles;
this.foundCaseFilesByParticipant = response.data.caseFilesSearchResult.foundByPrivateInfo;
if(this.foundCaseFiles.length > 0) {
this.lookupStatus = 'success';
} else {
this.lookupStatus = 'notFound';
}
}).catch(() => this.lookupStatus = 'error');
}
},
};
</script>
</style>
Add this to your TicketRunner.vue Component script:
computed: {
...mapGetters(['currentTicketCaseFiles'])
}

Render named scopedSlot programmatically

I want to move the following template into the render function of my component, but I don't understand how.
This is my template:
<template>
<div>
<slot name="item" v-for="item in filteredItems" :item="item">
{{ item.title }}
</slot>
</div>
</template>
This is my component:
export default {
props: {
items: {
type: Array,
required: true,
},
search: {
type: String,
default: ""
}
},
methods: {
filterByTitle(item) {
if (!("title" in item)) { return false; }
return item.title.includes(this.search);
}
},
computed: {
filteredItems() {
if (this.search.length === "") {
return this.items;
}
return this.items.filter(this.filterByTitle);
}
},
render: function(h) {
// How can I transform the template so that it finds its place here?
return h('div', ...);
}
};
I thank you in advance.
To render scoped slots you can use $scopedSlots. See more here.
Example Code:
...
render(h) {
return h(
'div',
this.filteredItems.map(item => {
let slot = this.$scopedSlots[item.title]
return slot ? slot(item) : item.title
})
)
}
...
JSFiddle

How to solve vue bind by methods like this

Look.
When data_a changes, getDataB will execute.
How to deal with this problem?
<template>
<div>
<div :data-a="data_a">
demo show params A
</div>
<div :data-b="getDataB()">
demo show params B
</div>
</div>
</template>
<script>
export default {
data () {
return {
data_a: 0,
datas: [
0, 1, 2, 3, 4
]
}
},
methods: {
getDataB () {
console.log('getDataB() called');
}
},
mounted () {
setInterval(function () {
this.data_a = parseInt(Math.floor(Math.random() * 10000000000));
}.bind(this), 1000);
}
}
</script>
First of all - switch to ES6 and avoid bind.
mounted () {
setInterval( () => {
this.data_a = parseInt(Math.floor(Math.random() * 10000000000));
}, 1000);
}
Then modify getDataB to make it computed:
computed: {
dataB () {
// do something with data_a
console.log('calc based on this.data_a in progress...')
return this.data_a*2 // ;)
}
},