how to use vue-glide-js events on nuxt and load more slides on last one - carousel

i'm new on all of these so i need help. first of all how vue-glide-js events work to begin with. its documentation just listed them without examples.
And second and more important, i wanna send an axios request at the end of my carousel and load more items. i couldn't work with events so used active slide and watched it and on last slide sent and updated my data but slide still shows the previous one. what should i do?
it is the simplified version of my code
<template>
<div>
<vue-glide v-bind="carouselOptions" v-model="active">
<vue-glide-slide v-for="i in array" :key="i">
Slide {{ i }}
</vue-glide-slide>
</vue-glide>
<p>{{active}}</p>
</div>
</template>
<script>
export default {
data(){
return{
carouselOptions:{
direction: process.env.SITE_DIRECTION,
autoplay: false,
perView: this.$device.isMobileOrTablet ? 4 : 8,
peek: { before: 0, after: 50 }
},
array: [1,2,3,4,5,6,7],
active: 0
}
},
watch:{
active(){
console.log('active = ' + this.active)
if(this.active > this.array.length - 3){
this.array= [1,2,3,4,5,6,7,8,9,10,11,12,13,14]
}
}
},
mounted(){
//
}
}
</script>
as you can see i manually added more items to my array but glide still show 7 items. i know it has something to do with mounting but dont know what to do.
and one more thing. if there is any better carousel that support rtl and breakpoint (items per view on different width) i would be happy to know. tanx

To use call a method at the end of slides just using #glide:run-end:
<template>
<div>
<vue-glide v-bind="carouselOptions" v-model="active" #glide:run-end="myMethod">
<vue-glide-slide v-for="i in array" :key="i">
Slide {{ i }}
</vue-glide-slide>
</vue-glide>
</div>
</template>
to show the new slides, it's a bit tricky! as far as i know the only way is to refresh (reload) the component! so put a key on vue-glide and change it after each new added slides. the only problem is, if there is images on slides, old ones also will be load again. (it's a suggestion and didn't tried my self yet, but maybe with a caching system, it may be solved)

Related

Vue class components dynamically add component depending on answer from backend

So from the backend I get a array of objects that look kind of like this
ItemsToAdd
{
Page: MemberPage
Feature: Search
Text: "Something to explain said feature"
}
So i match these values to enums in the frontend and then on for example the memberpage i do this check
private get itemsForPageFeatures(): ItemsToAdd[] {
return this.items.filter(
(f) =>
f.page== Pages.MemberPage &&
f.feature != null
);
}
What we get from the backend will change a lot over time and is only the same for weeks at most. So I would like to avoid to have to add the components in the template as it will become dead code fast and will become a huge thing to have to just go around and delete dead code. So preferably i would like to add it using a function and then for example for the search feature i would have a ref on the parent like
<SearchBox :ref="Features.Search" />
and in code just add elements where the ItemsToAdd objects Feature property match the ref
is this possible in Vue? things like appendChild and so on doesn't work in Vue but that is the closest thing i can think of to kind of what I want. This function would basically just loop through the itemsForPageFeatures and add the features belonging to the page it is run on.
For another example how the template looks
<template>
<div class="container-fluid mt-3">
<div
class="d-flex flex-row justify-content-between flex-wrap align-items-center"
>
<div class="d-align-self-end">
<SearchBox :ref="Features.Search" />
</div>
</div>
<MessagesFilter
:ref="Features.MessagesFilter"
/>
<DataChart
:ref="Features.DataChart"
/>
So say we got an answer from backend where it contains an object that has a feature property DataChart and another one with Search so now i would want components to be added under the DataChart component and the SearchBox component but not the messagesFilter one as we didnt get that from the backend. But then next week we change in backend so we no longer want to display the Search feature component under searchbox. so we only get the object with DataChart so then it should only render the DataChart one. So the solution would have to work without having to make changes to the frontend everytime we change what we want to display as the backend will only be database configs that dont require releases.
Closest i can come up with is this function that does not work for Vue as appendChild doesnt work there but to help with kind of what i imagine. So the component to be generated is known and will always be the same type of component. It is where it is to be placed that is the dynamic part.
private showTextBoxes() {
this.itemsForPageFeatures.forEach((element) => {
let el = this.$createElement(NewMinorFeatureTextBox, {
props: {
item: element,
},
});
var ref = `${element.feature}`
this.$refs.ref.appendChild(el);
});
}
You can use dynamic components for it. use it like this:
<component v-for="item in itemsForPageFeatures" :is="getComponent(item.Feature)" :key="item.Feature"/>
also inside your script:
export default {
data() {
return {
items: [
{
Page: "MemberPage",
Feature: "Search",
Text: "Something to explain said feature"
}
]
};
},
computed: {
itemsForPageFeatures() {
return this.items.filter(
f =>
f.Page === "MemberPage" &&
f.Feature != null
);
}
},
methods: {
getComponent(feature) {
switch (feature) {
case "Search":
return "search-box";
default:
return "";
}
}
}
};

Updating a d3 Chart with Vue on chage to data

This is the first time I'm asking a question here, so I hope I can phrase it in a way that makes sense.
I'm just beginning to learn Vue and D3, and I'm making an app that generates a bar chart based on some user data. It is supposed to display a chart representing one user, and then have a list of buttons that you can click to generate the chart that represents each of the other users. Right now, it can generate a chart for each different set of data, but I can't figure out how to make the chart update when a new user is chosen.
The name in the H2 header at the top of the chart updates when bottons are clicked, so I know my "featuredUser" prop is changing, so the buttons with usernames seem to be working (they are in another component):
<template>
<div id="Chart">
<h2>{{ featuredUser.firstName }} {{ featuredUser.lastName }}</h2>
<div class="Chart"></div>
</div>
</template>
<script>
import * as d3 from 'd3';
export default {
props: ["featuredUser"],
name: "Chart",
watch: {
featuredUser() {
this.generateChart();
// the below console log works, even when the chart doesn't update
// so it seems that this varaible is being watched for changes
console.log(this.featuredUser.firstName);
}
},
methods: {
generateChart() {
let qualities = this.featuredUser.qualities;
// EDIT: Adding the following two lines solves the problem
// the remove the previous chart before the new one is generated
d3.select(".Chart")
.selectAll('div').remove();
d3.select(".Chart")
.selectAll('div')
.data(qualities)
.enter().append('div')
.style('width', function (d) { return (d.score * 5)+10 + "em"})
.text(function (d) { return d.quality })
.attr("id", function(d) {return d.type});
},
},
// having the below as 'setup()' allows the chart to be generated on click
// for one user but it doesn't change when another user is clicked,
// having it set as 'mounted()' generates the chart of the chosen user on load,
// but it will not change again.
setup() {
this.generateChart();
}
};
</script>

Vue Warning on Console : You may have an infinite update loop in a component render function

I'm a new student of Computer Science and I have a school project to do on Vue.js.
It is working, but it shows the following Warning:
[Vue warn]: You may have an infinite update loop in a component render function.
English is not my first language, so I'm sorry if I write anything wrong in advance, but I'll try to explain what I have to do on the project.
I have to create a Photo Grid/Album that shows no more than 3 photos on each line. Something like this:
Example of how it has to look
The code is like this:
<template>
<v-container>
<!-- 'v-container' est´substituindo ' div class="clubes-lista" '-->
<div class="grid-photo">
<v-container class="grey lighten-3">
<v-row
v-for="index in numberLines()"
:key="index"
class="grey lighten-3"
>
<v-col v-for="n in 3" :key="n" cols="4">
<v-card v-if="numberPhotos > checkInsertedAllPhotos()" outlined>
<v-img
src="https://i.pinimg.com/736x/96/28/b7/9628b7892fd0543782f53eeec4bae502.jpg"
alt="Bird Photo"
>
</v-img>
<div class="photo-subtitle">
<v-icon size="15">mdi-heart-outline</v-icon>
<p>The Cockatiel</p>
</div>
</v-card>
<v-card v-else></v-card>
</v-col>
</v-row>
</v-container>
</div>
</v-container>
<script>
export default {
name: "PhotoGrid",
data() {
return {
listPhotosOnGrid: [],
numberPhotos: 0,
numberTotalLines: 0,
countPhotos: 0,
};
},
computed: {},
methods: {
createPhotosList() {
var listPhotos = [];
for (var i = 0; i < 10; i++) {
var aux = {
id: i + 1,
src:
"https://i.pinimg.com/736x/96/28/b7/9628b7892fd0543782f53eeec4bae502.jpg",
subtitle: "The cockatiel",
};
listPhotos.push(aux);
}
this.listPhotosOnGrid = listPhotos;
this.numberPhotos = listPhotos.length;
},
numberLines() {
this.createPhotosList();
var photosMultipleThree = this.numberPhotos;
while (photosMultipleThree % 3 != 0) {
photosMultipleThree = photosMultipleThree + 1;
}
this.numberTotalLines = photosMultipleThree / 3;
return photosMultipleThree / 3;
},
checkInsertedAllPhotos() {
var cont = this.countPhotos++;
return cont;
},
},
};
<style scoped>
.photo-subtitle {
font-size: 10px;
display: flex;
flex-direction: row;
justify-content: left;
align-items: flex-start;
padding: 0;
margin-top: 10px;
}
.photo-subtitle p {
margin-left: 5px;
}
So... Trying to figure out what is happening, I think the Warning comes from the following line:
<v-card v-if="numberPhotos > checkInsertedAllPhotos()" outlined>
The if is because it can't show more images that what is given. For example, each line shows 3 photos, so if I don't have a total of photos that is multiple os 3, I don't want to fill all columns on the last line, only the necessary to show all the photos.
But I just think, based on some tests, I'm not sure if the Warning comes from this line.
The code works, but I feel something is not right. (It works partialy actually, because I still don't know how to get the info from the array to show, so it only loops the same thing)
I want to learn how to make it right.
So does anyone can tell me what I can do to make the Warning disappear or if I have to change everything, and make make a new logic for it to work properly.
I just want to learn.
Thank you so, so much and sorry again if this is a stupid question, or if I'm making everything wrong, hehe.
And sorry if I wrote anything wrong.
:)
I've had a look to your code. First I would recommend you to stop using the var keyword but use let or const instead (you may check the question here and have a look at the scope based on those keyword, the var scope is very unusual nowadays and it will lead you to some issues).
Regarding your particular issue of infinite update loop, the problem comes from your checkInsertedAllPhotos function. I would explain it like this :
In the v-if Vue internal mechanisms checks if the element should be rendered by triggering the function checkInsertedAllPhotos
When executing the function checkInsertedAllPhotos the data countPhotos is increased (by the way it would be better to just do
this.countPhotos ++; return this.countPhotos;
Since the Data has been modified Vue is triggering a re-render of the page
Since there is a re-render Vue checks in the v-if if the element should be rendered by triggering the function checkInsertedAllPhotos.
And it could be infinite if Vue doesn't prevent it !
I would suggest to re-work a little bit your logic indeed. However a quick solution for your problem would be that when you're checking an index in your list : for example if you want to display listPhotosOnGrid[i], you check if i<numberPhotos with a v-if and in the v-else you can display something else if you want.
I also think you could have a much simpler code, by leveraging css (for example css-grid). You can have something as simple as this :
<div v-for="n in 100">
<img v-bind:src="`https://i.pinimg.com/photos/${n}.png`">
</div>
and some css to style it and only have 3 items per rows. If you want to learn about css-grid I would recommend Mozzila documentation or the following tutoriel.
Right now I have a little trouble understanding the exact question, since there are multiples topics inside to solve but I hope my explanation on the infinite loop will help you !
Have fun while learning Vue !
Thank you everyone who helped me, and you sure helped me!!!!
I'm sorry I could only anwser today.
I think I was able to do much better on the code, I changed it a lot, specially the logic on the code. And I think it can get even better, but for sure I already made a big progress, hehe.
I was a bit reluctant of posting this question because even though that as the time that I posted it I wasn't sure of what to do, I kinda of had the feeling that was something stupid (like a stuid question), and that I would only be taking the time of anyone who stopped by my question. But I only got amazing answers, with everyone being so patient, so THANK YOU SO, SO MUCH!!
(I'm also new to StackOverflow, so I hope I'm making everything right, hehe)

Vuetify: How to show overlay with spinner while v-data-table renders (how to wait for a component to finish rendering)

I have a Vuetify v-data-table with about a 1000 rows in it. Rendering it for the first time and when text searching on it, the render takes a few seconds. During this time I want to overlay it with a scrim + spinner to indicate things are happening.
The overlay:
<v-overlay
:value="loading"
opacity="0.7"
absolute
v-mutate="onMutate"
>
<v-progress-circular indeterminate size="32" color="blue"></v-progress-circular>
</v-overlay>
The v-data-table:
<v-data-table
:headers="cHeaders"
:items="cLimitedData"
disable-pagination
hide-default-footer
:search="searchIntercepted"
#current-items="tstCurrentItems"
>
The computed variable cLimitedData:
cLimitedData() {
if (this.indNoLimit) {
return this.data
} else {
return this.data.slice(0,this.dtRowTo)
}
},
I watch the search variable, and when it changes, I set loading to true to activate the overlay.
watch: {
search() {
if (!this.indNoLimit) {
// remove limit, this will cause cLimitedData to return all rows
this.loading = true
// -> moved to onMutate
//this.$nextTick(function () {
// this.indNoLimit = true
//})
} else {
this.searchIntercepted = this.search
}
},
However, the overlay doesn't activate until after the v-data-table had finished rendering. I've tried a million things, one of them is to put a v-mutate="onMutate" on the overlay, and only when it fired, would I this.indNoLimit = true to set things in motion, but that is still not good enough to have the scrim start before the v-data-table begins reloading itself.
onMutate(thing1) {
console.log('#onMutate',thing1)
this.$nextTick(function () {
this.indNoLimit = true
this.searchIntercepted = this.search
})
},
I also found that the next tick in #current-items fairly reliably marked the end of the render of the v-data-table, thus the deactivation of the scrim is probably going to be ok:
tstCurrentItems(thing1) {
console.log('#current-items',thing1)
this.$nextTick(function () {
console.log('#current-items NEXT')
this.loading = false
})
I believe my question should be: how can I detect/wait for components to have rendered (the v-overlay+v-progress-circular) before making changes to other components (the v-data-table).
Note: To solve the initial wait time of loading of the table, I found a way to progressively load it by inserting div-markers that trigger a v-intersect. However, this does not solve the situation when a user searches the whole data set when only the first 50 rows are loaded.
EDIT: Tried to start the update of the table after the overlay has been activated using https://github.com/twickstrom/vue-force-next-tick, but still no luck. It almost looks like vue tries to aggregate changes to the DOM instead of executing them one by one.
I have not figured out why the v-data-table seems to block/freeze, but here is a solution that can streamline any large v-data-table:
The v-data-table:
<v-data-table
:headers="cHeaders"
:items="data"
:items-per-page="dtRowTo"
hide-default-footer
hide-default-header
:search="search"
item-key="id"
>
<template
v-slot:item="{item, index, isSelected, select, headers}"
>
<tr>
<td
:colspan="headers.length"
>
Stuff
<div v-if="(index+1)%25==0" v-intersect.quiet.once="onIntersect">{{index+1}}</div>
</td>
</tr>
</template>
</v-data-table>
Add a div, or any other element, to intersect with at given intervals. Every time we intersect with it, we're going to increase the page size.
Variables:
dtRowTo: 50, // initial nr of rows
dtRowIncr: 50, // increments
The onIntersect:
onIntersect (entries, observer) {
let index = entries[0].target.textContent
if (index == this.dtRowTo) {
this.dtRowTo += this.dtRowIncr
}
},
Unfortunately you cannot add the index as an argument like v-intersect.quiet.once="onIntersect(index)", as this executes the function before you intersect with it (not sure why), so we will have to take the index out of the textContext.
Basically what you do is you increase the page size every time you're at the bottom. Search will still operate on the whole dataset.
What does not work (which I found out the hard way), is incrementally increasing the items, like the computed function below. As search needs the entire dataset to be present at :items, this won't work:
<v-data-table
:items="cLimitedData"
computed: {
cLimitedData() {
return this.data.slice(0,this.dtRowTo)
},
}
Doing so is fine (I guess?) as long as you don't need search or anything that operates on the entire data set.

A-Frame+Vue: can't bind "visible" attribute of a a-box

I wanted to see how easily A-Frame and Vue can work together.
One of the example I met with a google search is this fiddle: https://jsfiddle.net/baruog/23sdtzgx/
But I didn't like the fact that, to change the properties of the a-box in the example, the functions needed to access the DOM.
Like, for example, in this function:
setBoxColor: function(color) {
document.querySelector('a-box').setAttribute('color', color)
},
So, I wondered, can I bind the attributes of the a-box and change them without accessing to the DOM?
And I changed the code as in this other fiddle: https://jsfiddle.net/fy83wr49/
that I copy below:
The HTML
<div id="vue-app">
<a-scene embedded>
<a-sky color="#000"></a-sky>
<a-entity camera look-controls wasd-controls position="0 1 3" rotation="-15 0 0"></a-entity>
<a-box v-bind:color="color_box" v-bind:opacity="op_box" v-bind:visible="v_box"></a-box>
</a-scene>
<p>Click a button to change the color of the box</p>
<div>
<button #click="setBoxColor('red')">Red</button>
<button #click="setBoxColor('blue')">Blue</button>
<button #click="setBoxColor('green')">Green</button>
<button #click="setVisibility(true)">True</button>
<button #click="setVisibility(false)">Flase</button>
<button #click="changeOpacity()">Opacity</button>
</div>
</div>
And the JS
Vue.config.ignoredElements = ['a-scene', 'a-sky'];
var colorButtons = new Vue({
el: '#vue-app',
data: {
color_box: "magenta",
v_box: false,
op_box: 0.5,
},
methods: {
setBoxColor: function(color) {
this.color_box = color;
},
setVisibility: function(isVisible) {
this.v_box = isVisible;
//document.querySelector('a-box').setAttribute('visible', isVisible)
},
changeOpacity: function() {
this.op_box += 0.1;
if (this.op_box > 1.0) this.op_box = 0.0;
}
}
})
What happens is that both the "color" binding and the "opacity" binding work properly, but the "visible" binding doesn't.
Initially I thought that maybe the bindings were supposed to work only with standard html attributes, and it working with the "color" attribute of the a-box was just a coincidence caused by a name collision.
But then I checked the html attributes list here https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes and "opacity" isn't listed, so I had to abandon that explanation.
Does anyone have an idea of the reasons that make only the first two bindings work?
According to a-frame docs: https://aframe.io/docs/0.8.0/components/visible.html#updating-visible - it seems that visible is a special attribute which needs to be updated using object3D or setAttribute on the element. Actually they call it 'component' visible - not an attribute, while opacity and color seems to be just an attributes. Simple vue binding seems to not work to 'visible component'.