vue infinite load duplicate content issue - javascript

i install vue infinite loading for my chat app (backend laravel), everything is work now but if data respone from server contain something like a image or file, it seem to be load again and again like my image highlight bellow, correct the pic is load just one.
anyone can help with this issue? thanks!
infiniteHandler($state) {
setTimeout(() => {
axios.get('/messages', {
params: {
page: this.page,
room_id:this.user[0].shop_id === 0?this.user[0].id : this.user[0].shop_id
},
}).then(({ data }) => {
if (data.data.length) {
this.page += 1;
this.messages.unshift(...data.data.reverse());
$state.loaded();
} else {
$state.complete();
}
});
}, 200);
},
<div class="card-body" style="overflow: auto; height: 60vh; position: relative;" ref="card" #scroll="onScroll" >
<infinite-loading direction="top" #infinite="infiniteHandler"> </infinite-loading>
<div class="media" v-for="(message, $index ) in messages" :key="$index">
</div>
</div>

Try to set a different :key in your v-for. This is important for Vue to know which element is already rendered, and which should be re-rendered.
I don't know what your data looks like but you should have a uniq id for every items, or a name...
Using the index on dynamic lists will likely cause unecessary re-renders.
<div
class="media"
v-for="message in messages"
:key="message.id"
>
...
</div>

i remove vue-infinite-loading and end up with this solution Preserve scroll position on DOM update in vue.js . everything work fine now!

Related

Pagination works but doesn't reattach javascript / css class on reload

Im building a new personal blog and I'm using ajax to post back to a C# Controller to get the results for pagination.
Page 2 loads with the results however, none of the javascript is reloaded because, I believe, when I partially reload the pagination part of the page, it destroys everything in the DOM and because the full page doesn't reload, the javascript isn't invoked.
So I'm looking for a bit of help on working out how to get the external javascript to run again. What it does is adds css classes, gives some fade effects etc.
success: function (data) {
if (data != null) {
var page = data
$('#blogsContainer').empty();
$('#blogsContainer').replaceWith(page);
So the success works, I clear out the blogsContainer with the new data.
I'm guessing I need to add a function after the replace to then apply everything that is in an external main.js file.
The main.js file looks like this
(function($) {
var contentWayPoint = function() {
var i = 0;
$('.ftco-animate').waypoint( function( direction ) {
if( direction === 'down' && !$(this.element).hasClass('ftco-animated') ) {
i++;
$(this.element).addClass('item-animate');
setTimeout(function(){
$('body .ftco-animate.item-animate').each(function(k){
var el = $(this);
setTimeout( function () {
var effect = el.data('animate-effect');
if ( effect === 'fadeIn') {
el.addClass('fadeIn ftco-animated');
} else if ( effect === 'fadeInLeft') {
el.addClass('fadeInLeft ftco-animated');
} else if ( effect === 'fadeInRight') {
el.addClass('fadeInRight ftco-animated');
} else {
el.addClass('fadeInUp ftco-animated');
}
el.removeClass('item-animate');
}, k * 50, 'easeInOutExpo' );
});
}, 100);
}
} , { offset: '95%' } );
};
contentWayPoint();
}
The first page has the following applied to it on page load:
<div class="col-md-4 d-flex ftco-animate fadeInUp ftco-animated">
<div class="blog-entry justify-content-end">
...
</div>
</div>
But as you can see, when I press page 2, the div is missing some key css
<div class="col-md-4 d-flex ftco-animate">
<div class="blog-entry justify-content-end">
</div>
</div>
How would I apply the missing css after the partial reload with ajax?
I hope this is clear what I am trying to do but if not, please just ask.
I think the solution may be to re-execute the contentWayPoint() function at the end of the success callback. However, its likely out of scope by then. There are two simple ways to ensure its not :
The cleanest would be to ensure that the code that sets up your pagination is inside the same (function($) {}) block in main.js - that way it will "capture" the function.
The other, dirtier way, would be to change var contentWaypoint= function... to window.contentWaypoint = function - then use window.contentWaypoint() whenever you need to invoke it. THere are much better ways to doing this, but that might get you going.

How to pass dynamic data to a single bootstrap-vue modal instance and reuse it multiple times inside a loop?

I am trying to pass data to a single modal instance and reuse it multiple times inside a loop. It is working but whenever I'm clicking over an image, I'm getting an error - You may have an infinite update loop in a component render function.
This is the html where I'm looping over an array of images and passing it to a modal instance.
<li v-for="(source, i) in sources()" :key="i+'K'">
<span v-b-modal.fancybox #click="fancyBox(source)">
<p>click me</p>
</span>
</li>
This is the html for bootstrap-vue modal -
<b-modal
id="fancybox"
:modal-class="myclass"
hide-footer
centered
>
<img class="fancybox" :src="resource">
</b-modal>
Following is the vue js code -
export default {
data: () => ({
myclass: ['myclass'],
srcItems: [],
resource: ''
}),
methods: {
sources () {
this.srcItems = [
'https://picsum.photos/id/0/5616/3744',
'https://picsum.photos/id/1/5616/3744',
'https://picsum.photos/id/10/2500/1667',
'https://picsum.photos/id/100/2500/1656',
'https://picsum.photos/id/1000/5626/3635'
]
return this.srcItems
},
fancyBox (source) {
this.resource = source
}
}
}
I couldn't able to figure out what is wrong here! Please help me. Thank you for your time.
Hope this will help you to get started
**[LIVE DEMO](https://codepen.io/boussadjra/pen/xxERwZm)**

How does <transition-group> work with Vue.components?

Suppose I want to use the components to make a list that would disappear if I click on it, and use the transition-group to do the animation part.
The following code can perform well:
HTML:
<transition-group name="testanim">
<p key="1" v-if='open1' #click='open1 = false'>Can You See Me?</p>
<p key="2" v-if='open2' #click='open2 = false'>Can You See Me?</p>
</transition-group>
CSS:
.testanim-enter-active, .testanim-leave-active {
transition: all .5s;
}
.testanim-enter, .testanim-leave-to {
transform: translateX(1rem);
opacity: 0;
}
.testanim-leave-active {
position: absolute;
}
.testanim-move {
transition: all .5s;
}
open1 and open2 are defined in data in Vue.js.
However, the following code would not perform the animation at all.
HTML:
<transition-group name="testanim">
<test-sth key="1"></test-sth>
<test-sth key="2"></test-sth>
</transition-group>
CSS: the same with above
JavaScript:
Vue.component ("test-sth", {
template: "<p v-if='open' #click='open = !open'>Can You See Me?</p>",
data: function () {
return {
open: true,
}
}
})
So the problem is that how I can animate the components inside the transition-group. I've searched for a few hours but did not find some question or documents related to it.
Update:
The key problem is that the animation in the former example that the second sentence move upwards smoothly when the first sentense disappear do not show in the latter one. Although I may put the transition inside the template, That do not solve the problem.
Should I write the whole transition-groupinside the template, or something else...?
When using Vue transitions, for internal reasons, the transition/transition-group components must be in the same template as the state that's being toggled.
Also, Vue components require that there always be a single root element for a component. A v-if breaks this rule because it gives the possibility of the element not being there, if the v-if happens to be false.
To solve your issue, move the transitioning to the test-sth component. Since it manages its own toggling, it should manage its own transitioning as well.
Vue.component("test-sth", {
template: `
<transition name='testanim'>
<p v-if='open' #click='open = !open'>Can You See Me?</p>
</transition>
`,
data: () => ({
open: true,
}),
})
new Vue({
el: "#app",
template: `
<div>
<test-sth></test-sth>
<test-sth></test-sth>
</div>
`,
})
See this fiddle for a working example.

The content not shown properly in function callback in Vue.js

I've got two problems here. The first is that I can't get the star rendered properly. I can do it if I change the value in the data() function but if I want to do it in a function callback way, it doesn't work (see comments below). What's going wrong here? Does it have something to do with Vue's lifecycle?
The second one is that I want to submit the star-rate and the content of the textarea and when I refresh the page, the content should be rendered on the page and replace the <textarea></textarea> what can I do?
I want to make a JSFiddle here but I don't know how to make it in Vue's single-file component, really appreciate your help.
<div class="order-comment">
<ul class="list-wrap">
<li>
<span class="comment-label">rateA</span>
<star-rating :data="dimensionA"></star-rating>
</li>
</ul>
<div>
<h4 class="title">comment</h4>
<textarea class="content" v-model="content">
</textarea>
</div>
<mt-button type="primary" class="mt-button">submit</mt-button>
</div>
</template>
<script>
import starRating from 'components/starRating'
import dataService from 'services/dataService'
export default {
data () {
return {
dimensionA: '' //if I changed the value here the star rendered just fine.
}
},
components: {
starRating
},
methods: {
getComment (id) {
return dataService.getOrderCommentList(id).then(data => {
this.dimensionA = 1
})
}
},
created () {
this.getComment(1) // not working
}
}
</script>
What it seems is scope of this is not correct in your getComment method, you need changes like following:
methods: {
getComment (id) {
var self = this;
dataService.getOrderCommentList(id).then(data => {
self.dimensionA = 1
})
}
},
As you want to replace the <textarea> and render the content if present, you can use v-if for this, if content if available- show content else show <textarea>
<div>
<h4 class="title">comment</h4>
<span v-if="content> {{content}} </span>
<textarea v-else class="content" v-model="content">
</textarea>
</div>
See working fiddle here.
one more problem I have observed in your code is you are using dynamic props, but you have assigned the prop initially to the data variable value in star-rating component, but you are not checking future changes in the prop. One way to solve this, assuming you have some other usage of value variable is putting following watch:
watch:{
data: function(newVal){
this.value = newVal
}
}
see updated fiddle.

Meteor: Hide or remove element? What is the best way

I am quite new with Meteor but have really been enjoying it and this is my first reactive app that I am building.
I would like to know a way that I can remove the .main element when the user clicks or maybe a better way would be to remove the existing template (with main content) and then replace with another meteor template? Something like this would be simple and straightforward in html/js app (user clicks-> remove el from dom) but here it is not all that clear.
I am just looking to learn and for some insight on best practice.
//gallery.html
<template name="gallery">
<div class="main">First run info.... Only on first visit should user see this info.</div>
<div id="gallery">
<img src="{{selectedPhoto.url}}">
</div>
</template>
//gallery.js
firstRun = true;
Template.gallery.events({
'click .main' : function(){
$(".main").fadeOut();
firstRun = false;
}
})
if (Meteor.isClient) {
function showSelectedPhoto(photo){
var container = $('#gallery');
container.fadeOut(1000, function(){
Session.set('selectedPhoto', photo);
Template.gallery.rendered = function(){
var $gallery = $(this.lastNode);
if(!firstRun){
$(".main").css({display:"none"});
console.log("not");
}
setTimeout(function(){
$gallery.fadeIn(1000);
}, 1000)
}
});
}
Deps.autorun(function(){
selectedPhoto = Photos.findOne({active : true});
showSelectedPhoto(selectedPhoto);
});
Meteor.setInterval(function(){
selectedPhoto = Session.get('selectedPhoto');
//some selections happen here for getting photos.
Photos.update({_id: selectedPhoto._id}, { $set: { active: false } });
Photos.update({_id: newPhoto._id}, { $set: { active: true } });
}, 10000 );
}
If you want to hide or show an element conditionaly you should use the reactive behavior of Meteor: Add a condition to your template:
<template name="gallery">
{{#if isFirstRun}}
<div class="main">First run info.... Only on first visit should user see this info.</div>
{{/if}}
<div id="gallery">
<img src="{{selectedPhoto.url}}">
</div>
</template>
then add a helper to your template:
Template.gallery.isFirstRun = function(){
// because the Session variable will most probably be undefined the first time
return !Session.get("hasRun");
}
and change the action on click:
Template.gallery.events({
'click .main' : function(){
$(".main").fadeOut();
Session.set("hasRun", true);
}
})
you still get to fade out the element but then instead of hiding it or removing it and having it come back on the next render you ensure that it will never come back.
the render is triggered by changing the Sessionvariable, which is reactive.
I think using conditional templates is a better approach,
{{#if firstRun }}
<div class="main">First run info.... Only on first visit should user see this info.</div>
{{else}}
gallery ...
{{/if}}
You'll have to make firstRun a session variable, so that it'll trigger DOM updates.
Meteor is reactive. You don't need to write the logic for redrawing the DOM when the data changes. Just write the code that when X button is clicked, Y is removed from the database. That's it; you don't need to trouble yourself with any interface/DOM changes or template removal/redrawing or any of that. Whenever the data that underpins a template changes, Meteor automatically rerenders the template with the updated data. This is Meteor’s core feature.

Categories