How to add displays without re-rendering the whole component in VueJS - javascript

I'm just new to Laravel and Vuejs. And I have this problem wherein the whole component is re-rendering when the "Load More" button is clicked or when scrolled down to the bottom. The button or scroll is just acting like a pagination, but all you can do is to load more or add more displays. My problem is how can i render the new displays without re-rendering the whole component.
I tried creating a variable wherein it will pass how many paginate will be displayed. Yes it does the work but the component is re-rendering and the size of the reply from the server gets larger and larger.
here's my script on my Vue component:
<script>
export default {
props: ['user','review_count'],
data(){
return{
reviews: {},
limit: 2,
scrolledToBottom: false,
}
},
created(){
this.getReviews();
this.scroll();
},
methods: {
getReviews: function(page){
axios.get('/datas/reviews?user='+ this.user + '&limit='+ this.limit)
.then((response) =>{
this.reviews = response.data.data;
})
.catch(()=>{
});
},
countType: function(data, type) {
return data.filter(function(value) { return value.type === type }).length;
},
loadMore: function(){
this.limit+=6;
this.getReviews();
},
scroll () {
window.onscroll = () => {
let bottomOfWindow = Math.max(window.pageYOffset, document.documentElement.scrollTop, document.body.scrollTop) + window.innerHeight === document.documentElement.offsetHeight
if (bottomOfWindow&&this.review_count>this.limit) {
this.loadMore();
}
}
}
}
}
</script>
here's my controller:
public function reviews()
{
if($users = \Request::get('user')){
if($limit = \Request::get('limit')){
$reviews = Review::select(\DB::raw('id, product_id, review_rating, review_content'))
->where('user_id', $users)
->with('products:product_image,id,product_name')
->with('activities')
->orderBy('reviews.created_at', 'DESC')
->paginate($limit);}}
return $reviews;
}

I just solved my own Question, the solution is using pagination. I just pushed all of the data from the server in an object every time it scrolled down.
here's my code for scroll down:
scroll () {
window.onscroll = () => {
let bottomOfWindow = Math.max(window.pageYOffset, document.documentElement.scrollTop, document.body.scrollTop) + window.innerHeight === document.documentElement.offsetHeight
if (bottomOfWindow&&this.review_count>this.reviews.length) {
this.getReviews();
}
}
}
here's my codes for getReviews():
getReviews: function(){
let vm = this;
axios.get('/datas/reviews?user='+ this.user + '&page='+ this.page)
.then((response) =>{
$.each(response.data.data, function(key, value) {
vm.reviews.push(value);
console.log((vm.reviews));
});
})
.catch(()=>{
});
this.page+=1;
},
I've come up with this idea so that I will not use pagination anymore to view the next posts. It's more like a infinite scroll pagination component.

In my opinion, there is two things you need to do.
1/ in stead of increasing the limit eveytime, you should look into paging your results on the server, so you can ask the next page from your server. Now, on each consecutive call you are fetching what you already had again, which will eliminate your goal of making this less stressful on your server
2/ in your client code obviously you need to support the paging as well, but also make sure you properly set your key on the looped elements. VueJS used the key to determine whether it should rerender that particular element in the loop.
Let me know if this helps!

Related

Window.scroll only working once (React.js)

I'm making a simple React component to remember where you are on the page and place you there when returning.
Here's the code:
function ScrollCache() {
window.scroll(0, parseInt(localStorage['scroll']));
document.addEventListener('scroll', function (e) {
if (window.scrollY != 0) {
localStorage['scroll'] = window.scrollY.toString();
}
})
return (<></>)
}
Basically, it caches the last known scroll position, and uses window.scroll(x, y) to scroll to that position. I have verified that localStorage is working as intended with a console.log immediately before the window.scroll. I've also just tried a static 100 for the y coordinate. No matter what, it only scrolls once at reload and then never again when I'm navigating around.
I'm using React Router to go between web pages. Any help is appreciated
You don't need to add the scroll event listener every time you want to cache the scroll.
Instead, try this:
const [scrollPosition, setScrollPosition] = useState(0);
useEffect(() => {
window.addEventListener("scroll", handleScroll, {
passive: true
});
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, [scrollPosition]);
useEffect(() => {
localStorage['scroll'] = scrollPosition.toString();
}, [scrollPosition);
const handleScroll = () => {
const position = window.pageYOffset;
setScrollPosition(position);
};

Vue.js Computed/Watch Not Reacting to Viewport Width Data Change

Can someone explain to me in simple terms why the following example doesn't work?
I'm trying to run a function that captures the viewport/window width and then runs code based on how wide the viewport or window is (responsive design).
I'm a beginner so it's entirely possible I'm misunderstanding how Watch and Computed works... but it's my understanding that both Watch and Computed monitors a data property and if my data changes, watch and computed should react and trigger their code right?
So if I have a value called viewportWidth in my data, and I run an onresize to continually update it, I am updating my data which should trigger my watcher right? Shouldn't the continually updating value also trigger my computed property since it also relies on changing data?
So far I'm not seeing either of them react to my data changing.. if I'm misunderstanding please ELI5 and show me the better way to approach this and why..
(quick sidenote: I understand I can just run my handler inside of my onresize listener, but I assumed it would be smarter to instead setup a watcher or computed so that my method since they cache(?) and not trigger too often when it doesn't need to and only update conditions when it needs to.. is that right?)
Thank you!
<template>
<main>
<section>
<h2>viewport width: {{viewportWidth}}px</h2>
<h2>computed: {{rackClass}}</h2>
<h2>Does it work? {{doesItWork}}</h2>
</section>
</main>
</template>
<script>
export default {
data() {
return {
viewportWidth: window.innerWidth,
doesItWork: 'no it does not'
}
},
mounted() {
window.onresize = function(e) {
this.viewportWidth = window.innerWidth;
console.log(window.innerWidth)
}
},
watch: {
viewportWidth: function() {
console.log('>> value changed')
this.handleViewPortChange();
}
},
computed: {
rackClass: function(){
let theValue = "greater";
if(this.viewportWidth > 1000) theValue = "less than"
console.log('>> viewportWidth changed = ',this.viewportWidth)
return theValue
},
methods:{
handleViewportChange: function() {
this.doesItWork = 'it works!';
}
}
}
}
</script>
https://codepen.io/cmaxster/pen/rNyZLXG
Well aren't you in a pickle!
You are putting your curly braces and the commas in all the wrong places!
I updated the code so that it can be added as a snippet here. I have also put comments where you had messed up.
const app = new Vue({
el: "#app",
data() {
return {
viewportWidth: window.innerWidth,
doesItWork: 'no it does not'
}
},
mounted() {
const self = this;
window.onresize = (e) => {
this.viewportWidth = window.innerWidth;
//console.log(window.innerWidth)
}
},
watch: {
viewportWidth: function() {
console.log('>> value changed')
this.handleViewportChange(); // you were calling the wrong method! spellings and case was messed up
}
},
computed: {
rackClass: function(){
let theValue = "greater";
if(this.viewportWidth > 1000) theValue = "less than"
console.log('>> viewportWidth changed = ',this.viewportWidth)
return theValue
}
},
// you had your methods inside computed!
methods:{
handleViewportChange() {
this.doesItWork = 'it works!';
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.12/dist/vue.js"></script>
<main id="app">
<section>
<h2>viewport width: {{viewportWidth}}px</h2>
<h2>computed: {{rackClass}}</h2>
<h2>Does it work? {{doesItWork}}</h2>
</section>
</main>
Have you tried transforming your watch into an arrow function?

this.router.routeReuseStrategy.shouldReuseRoute = () => false;

this.router.routeReuseStrategy.shouldReuseRoute = () => false;
I have applied this sort of line in order to make the component UI updated everytime. But in some other cases it start to refreshing the page event if it should reuse the route.
How can we overcome this issue?
Actually in my application there are three tabs in left panel. In each tab there are some listings clicking on list items opens the content on right panel. But in one of the listing there is a common UI that is getting open on some list item, but the problem is that when we don't apply above sort of code then the UI is not getting updated. But if we apply the code then the UI is updated everytime we click on other list item. But the problem is that when we apply this code it start to refresh the page everytime we click on other list in different tabs also, that should not be the case.
If we apply this code this.router.routeReuseStrategy.shouldReuseRoute = () => false; then how can we revert this functionality under this.router?
To take less risks I'm just reverting it back to what it was once the reload is done:
refresh() {
const prev = this.router.routeReuseStrategy.shouldReuseRoute;
const prevOSN = this.router.onSameUrlNavigation;
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';
this.router.navigate([this.router.url]);
setTimeout(() => {
this.router.routeReuseStrategy.shouldReuseRoute = prev;
this.router.onSameUrlNavigation = prevOSN;
}, 0);
}
I have the same issue, I changed that line for this:
// override the route reuse strategy
this.router.routeReuseStrategy.shouldReuseRoute = function () {
return false;
};
this.router.events.subscribe((evt) => {
if (evt instanceof NavigationEnd) {
// trick the Router into believing it's last link wasn't previously loaded
this.router.navigated = false;
// if you need to scroll back to top, here is the right place
window.scrollTo(0, 0);
}
});
I don't even know if this works well or do the same thing.
private saveRouterStrategyReuseLogic: any;
ngOnInit() {
// Save logic
this.saveRouterStrategyReuseLogic = this.router.routeReuseStrategy.shouldReuseRoute;
this.router.routeReuseStrategy.shouldReuseRoute = (future, curr) => { return false; };
}
ngOnDestroy() {
this.router.routeReuseStrategy.shouldReuseRoute =
this.saveRouterStrategyReuseLogic;
}

Vue Transition - JavaScript hooks

Based on this answer, I'm trying to create a Vue slideToggle component using transition.
The slideToggle is a classic paradigm in height animation. I've been successful so far...
I don't want to set a fixed max-height or height, I want the height to be dynamic.
My animation is working properly when displaying and hiding. The problem is in canceling while displaying or hiding.
How to handle the #enter-cancelled and the #leave-cancelled hooks? I'm new to vue transitions :)
I put my code inside this CodeSandBox: https://codesandbox.io/s/vue-template-3b7oj
I don't know if this helps you, but try this:
declare a new variable:
data() {
return {
height: null,
toggling: false
};
},
when the open or close function start, verify if toggling is true, if yes, just cancel, like this:
enter(el) {
if (this.toggling) return;
this.toggling = true;
this.height = el.offsetHeight;
el.style.overflow = "hidden";
el.style.height = 0;
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
el.style.marginTop = 0;
el.style.marginBottom = 0;
setTimeout(() => {
el.style.transitionProperty = `height, margin, padding`;
el.style.transitionDuration = this.duration + "ms";
el.style.height = this.height + "px";
el.style.removeProperty("padding-top");
el.style.removeProperty("padding-bottom");
el.style.removeProperty("margin-top");
el.style.removeProperty("margin-bottom");
this.toggling = false;
});
},
Will be something like this:
https://codesandbox.io/s/vue-template-78n7t?fontsize=14
Maybe i broke your code, sorry, but will you get the idea.
As per the offical documentation Javacript transition hooks
the #leave-cancelled is only available with v-show, where are in your sample code you are using v-if, if you change this you will be able to capture the #leave-cancelled hook,#leave-cancelled and #enter-cancelled are triggered when enter or leave are interrupted, say you press the toggle button while opening as well as pressing the button while its closing.
Vue-Transition-Cancel
tl;dr
leave event cancels not yet called enter
enter cancels not yet called leave
cancel state is stored in
el._enterCb.cancelled
el._leaveCb.cancelled
analysis
Consider:
const cb = el._enterCb = once(() => {
if (expectsCSS) {
removeTransitionClass(el, toClass)
removeTransitionClass(el, activeClass)
}
if (cb.cancelled) {
if (expectsCSS) {
removeTransitionClass(el, startClass)
}
enterCancelledHook && enterCancelledHook(el)
} else {
afterEnterHook && afterEnterHook(el)
}
el._enterCb = null
})
source: _enterCb
So a naive solution to cancel #enter is
el => {el._enterCb.cancelled = true; done()}
This is what actually happens when one triggers leave
// call enter callback now
if (isDef(el._enterCb)) {
el._enterCb.cancelled = true
el._enterCb()
}
source: leave
Same applies to:
const cb = el._leaveCb = once(() => {
if (el.parentNode && el.parentNode._pending) {
el.parentNode._pending[vnode.key] = null
}
if (expectsCSS) {
removeTransitionClass(el, leaveToClass)
removeTransitionClass(el, leaveActiveClass)
}
if (cb.cancelled) {
if (expectsCSS) {
removeTransitionClass(el, leaveClass)
}
leaveCancelled && leaveCancelled(el)
} else {
rm()
afterLeave && afterLeave(el)
}
el._leaveCb = null
})
source: _leaveCb
One can check for possible assignments:
https://github.com/vuejs/vue/search?q=_leaveCb&unscoped_q=_leaveCb

How to fix problem with not working updateServing function in JavaScript?

I'm trying to implement a function which would calculate the servings for the ingredients from my website.
That function is in Recipe.js file and looks like that:
updateServings(type) {
// Servings
const newServings = type === 'dec' ? this.servings - 1 : this.servings + 1;
// Ingredients
this.ingredients.forEach((ingr) => {
ingr.count = this.capDecimal(ingr.count * (newServings / this.servings));
});
this.servings = newServings;
}
The problem is that when I console.log(state.recipe); in index.js this event Listener works, it will console log state.recipe after clicking - or + button on the website but it wont change the amount of serving in the recipe object:
elements.recipe.addEventListener('click', e => {
if(e.target.matches('.btn-decrease .btn-decrease *')){
//Decrease button is clicked
if(state.recipe.servings > 1){
state.recipe.updateServings('dec');
}
}else if(e.target.matches('.btn-increase .btn-increase *')){
//Increase button was clicked
state.recipe.updateServings('inc');
}
console.log(state.recipe);
});
I clicked 2 times but property serving still says 4 like here:
https://forum.toshitimes.com/uploads/toshitimes/original/2X/6/6bada9081879db1a14df9bad010382606fda253f.png
It a bigger project so I believe I need to include the whole repository from github: https://github.com/damianjnc/forkifyApp.git
What I need to change to make it work?
You need to update the view after the click event
elements.recipe.addEventListener('click', e => {
//....
try {
recipeView.clearRecipe();
recipeView.renderRecipe(state.recipe);
} catch (error) {
alert('error processing the recipe:(');
}
});
note: you need to declare your class properties
export default class Recipe {
ingredients;
servings;
constructor(id) {
this.id = id;
}
and you need map instead of forEach
this.ingredients = this.ingredients.map((ingr) => {
ingr.count = this.capDecimal(ingr.count * (newServings / this.servings));
return ingr;
});

Categories

Resources