vuetify list perfomance issue - performance

I have a simple json array which consist of object like this
{
"kb:esid": "779256bf333d2d11abb52e39aafff20d",
"kb:station": "Disco935 New York's Jammer",
"kb:description": "Playing The 70's 80's 90's Disco And Beyond",
"kb:station_url_record": {
"kb:url_no": "1",
"kb:url": "http://localhost/stream",
"kb:status_code": "A",
"kb:bandwidth_kbps": "128",
"kb:url_strength": "9"
}
}
I use vuetify on codrova project to render list. The list consist of 3 lines: station, description and bandwidth. There is also a simple component to mark a station as a favourite.
The list rendering takes about 5 seconds to render ~200 elements. I'm using a bit old smartphone: 2GB memory, 5.1 Android, ~1.2 GHz CPU.
How can I increase the render speed? I tried to search infinity list in vuetify but there is nothing about it.
Thanks.

Vue.js is a little bit heavy to load however you can use lazy initialization by implementing an infinite scroll component. There is an implemented component called vue-mugen-scroll. It also has a CDN link if you need a quick test.
Just replace getStations method with your asynchronous server call and it is done. Check this snippet to see how it works.
let vm = new Vue({
el: '#vue-instance',
data: {
stations: [],
loading: false,
},
created() {
this.getStations();
},
methods: {
getStations() {
for (let i = 0; i < 16; i++) {
const count = this.stations.length + i;
this.stations.push({
esid: '779256bf333d2d11abb52e39aafff20d' + count,
name: 'Station ' + count,
description: 'Station Description ' + count,
record: 'Station Record ' + count,
});
}
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-mugen-scroll#0.2.5/dist/vue-mugen-scroll.min.js"></script>
<div id="vue-instance" class="container">
<div class="row">
<div class="col-sm-6" v-for="(station, index) in stations">
<div class="card m-4" style="width: 18rem;">
<div class="card-body">
<h3 class="card-title">{{ station.name }}</h3>
<p>{{ station.esid }}</p>
<p>{{ station.description }}</p>
<p>{{ station.record }}</p>
</div>
</div>
</div>
</div>
<mugen-scroll :handler="getStations" :should-handle="!loading">
loading...
</mugen-scroll>
</div>

Related

Framer motion new animation and exit not working with mapping

for some reason my exit and new animation is not working. I would like new animation to start every time user click on different menu link. I have also tried with " animate='visible'" , and I have tried also to put directly over motion, and it still not doing exit or starting new animation. I am using .map and framer motion together. Can someone please check it out.
This is the code
Thanks
const [forMapping, setForMapping] = useState(wines)
function menuHandler(index, alt) {
setIsActive(index)
if (alt === 'wine') {
setForMapping(wines)
} else if (alt === 'rakia') {
setForMapping(rakia)
}
}
const variants = {
visible: i => ({
y: 0,
transition: {
duration: .7
}
}),
hidden: {
y: '40%'
}
}
<AnimatePresence>
{forMapping.map((item, index) => {
const {
name,
description,
alt,
imageSrc,
price,
glass_price,
iconSrc,
alc
} = item;
return (
<motion.div
exit={{y: '100'}}
viewport={{once: true}}
custom={index}
whileInView='visible'
initial='hidden'
variants={variants}
key={index}
className='item'>
<div className="image">
<Image
width={200}
height={400}
objectFit='cover'
src={imageSrc}
alt={alt}/>
</div>
<div className="info">
<div className="info-header">
<header>
{name}
</header>
<p className="price">
{price.toFixed(2)} EUR
</p>
</div>
<p className="description">
{description}
</p>
<div className="bottom">
<p>
{alc} %VOL
</p>
<div className='image-price'>
<Image
width={18}
height={20}
objectFit='cover'
src={iconSrc}
alt='wine glass'/>
<p className="black">
{glass_price.toFixed(2)} EUR
</p>
</div>
</div>
</div>
</motion.div>
)
})}
</AnimatePresence>
You should not use the loop index as the key in your motion.div. Since the indices (and thus the keys) will always be the same, it doesn't let <AnimatePresence> track when elements have been added or removed in order to animate them.
Instead use a value like a unique id property for each element. This way React (and AnimatePresence) will know when it's rendering a different element (vs the same element with different data). This is what triggers the exit and enter animations.
Here's a more thorough explanation:
react key props and why you shouldn’t be using index

localStorage persisting with looped Vue component

I'm having the strangest time getting localStorage to work on my local machine. I seem to be losing indexes in the array I built, I've been trying for hours to figure out why with absolutely no luck. I've even tried different ways of building the array.
Here is the component I am loading with a v-for loop. this is working as expected.
home.vue
<tweets
v-for="tweet in tweets"
v-bind:key="tweet.id"
v-bind:tweet="tweet"
></tweets>
And here is the trouble-maker component. It loads a number of tweets that can be voted on. (Feel free to ignore the HTML, I'm not sure it's relevant.)
tweets.vue
<template>
<div class="col-2 d-flex">
<div class="align-self-center ml-3">
<div class="row py-1">
<i
class="fas fa-arrow-up"
style="font-size: 30px"
v-on:click="voteUp"
></i>
</div>
<div class="row py-1">
<i class="fas fa-arrow-down" style="font-size: 30px"></i>
</div>
</div>
</div>
</template>
<script>
export default {
data: function () {
return {
localStorage: [],
};
},
props: {
tweet: Object,
},
created: function () {
this.localStorage = JSON.parse(localStorage.storageData);
console.log(this.localStorage);
},
methods: {
voteUp: function () {
axios
.get("/api/vote/2/1")
.then(({ data }) => {
var test = {
"tweet_id": 1,
"vote_status": 1
};
this.localStorage.push(test);
console.log(this.localStorage);
localStorage.setItem("storageData", JSON.stringify(this.localStorage));
console.log("go");
//persist?
})
.catch(function (error) {
alert("Take a screen shot and send this to me." + error);
console.log(error);
});
},
},
};
</script>
So if you take a look at my localStorage variable, I have it being set to the current localStorage found in the browser, however...it's having this strange problem where if I click on the button that triggers the voteUp function, it will sometimes delete some of the indexes in the array. I'm having trouble explaining so I've make a quick video to demonstrate.
https://streamable.com/kkhnrx
as you can see, I'm firing the event and the array starts growing, but as I scroll down to different buttons (remember the tweets are looped) the array will lose a few of it's indexes. I have no idea why this is happening. I assume it has something to do with the component being looped and scope problems?
This looks exactly like race condition. There are multiple component instances that compete over the storage, and it becomes overwritten eventually.
This makes an early copy that won't contain updates from other instances:
this.localStorage = JSON.parse(localStorage.storageData)
this.localStorage should be assigned immediately before updating the storage, or there should be a common state for all component instances (e.g. by using Vuex with persistence).
You have an error in your created function, right?
If you want to get "storageData" from local storage it should be:
created: function () {
this.localStorage = JSON.parse(localStorage.getItem('storageData'));
console.log(this.localStorage);
},

How to use multiple fineuploader instances with manual upload buttons with one template

With the fine-uploader plugin I am trying to add multiple (dynamic could be 1, or 10) instances with an optional caption field and a manual upload button per section.
The form I am uploading from is dynamically generated in layout as well as content, the uploaded files have to be stored by the handler based upon the section of the form as well as the instance of fine-uploader. I also need the ability to effectively upload each instance of fine-uploader independently
The issue that I am hitting is following the guidelines & demo for the manual upload option, ie adding a click function it will always find only the first instance as it searches for the button using .getElementById.
I can get around this by defining a new template for each instance however I would prefer to use a single template.
The template code (for each instance - abbreviated for simplicity) is
<script type="text/template" id="qq-template-manual-trigger#XX#">
<div class="qq-uploader-selector qq-uploader" qq-drop-area-text="Drop files here">
...
<div class="buttons">
<div class="qq-upload-button-selector qq-upload-button">
<div>Select files</div>
</div>
<button type="button" id="trigger-upload#XX#" class="btn btn-primary">
<i class="icon-upload icon-white"></i> Upload
</button>
</div>
...
<ul class="qq-upload-list-selector qq-upload-list" aria-live="polite" aria-relevant="additions removals">
<li>
...
<input class="caption" tabindex="1" type="text">
...
</li>
</ul>
...
</div>
</script>
<div id="fine-uploader-manual-trigger#XX#"></div>
and the uploader script
<script>
var manualUploader#XX# = new qq.FineUploader({
element: document.getElementById('fine-uploader-manual-trigger#XX#'),
template: 'qq-template-manual-trigger#XX#',
request: {
inputName: "imagegroup[]",
endpoint: '/SaveFile.aspx'
},
autoUpload: false,
debug: true,
callbacks: {
onError: function(id, name, errorReason, xhrOrXdr) {
alert(qq.format("Error on file number {} - {}. Reason: {}", id, name, errorReason));
},
onUpload: function (id) {
var fileContainer = this.getItemByFileId(id)
var captionInput = fileContainer.querySelector('.caption')
var captionText = captionInput.value
this.setParams({
"descr[]": captionText,
<-- Other parameters here -->
}, id)
}
},
});
qq(document.getElementById("trigger-upload#XX#")).attach("click", function () {
manualUploader#XX#.uploadStoredFiles();
});
</script>
in the ideal world I would prefer simply have a single
<script type="text/template" id="qq-template-manual-trigger">
....
</script>
then where required multiple times through the form
<div id="fine-uploader-manual-trigger"></div>
<script>
var manualUploader#XX# = new qq.FineUploader({
element: document.getElementById('fine-uploader-manual-trigger'),
template: 'qq-template-manual-trigger',
...
}
qq(document.getElementById("trigger-upload")).attach("click", function () {
manualUploader#XX#.uploadStoredFiles();
});
</script>
The use of the attach function by calling .getElementById just feels wrong, or at the very least cludgy, is there a better way of activating the upload on a per-instance basis?
Thanks in advance
K
Sorted, but if anyone has a better answer...
Instead of using the demo of document.getElementById("trigger-upload")
Simply use document.querySelector("#fine-uploader-manual-trigger #trigger-upload")
eg
<div id="fine-uploader-manual-triggerXX"></div>
<script>
var manualUploaderXX = new qq.FineUploader({
element: document.getElementById('fine-uploader-manual-triggerXX'),
template: 'qq-template-manual-trigger',
... // omitted for brevity
}
qq(document.querySelector("#fine-uploader-manual-triggerXX #trigger-upload")).attach("click", function () {
manualUploaderXX.uploadStoredFiles();
});
</script>

Foundation tabs are not displaying if data is being dynamically retrieved from the database

I have a vue js application that fetches data from a database using ajax and presents them in a foundation tab. I have emulated everything as best I know how but unfortunately the content is not appearing.
My Vue component code is as below.
<template>
<section>
<pos-header></pos-header>
<div id="products">
<ul class="tabs" data-tabs id="product-types">
<li class="tabs-title" v-for="type, index in products.types" :class="{ 'is-active': index == 0}">
<a :href="'#' + type.name">
{{ type.name }}
</a>
</li>
</ul>
<div class="tabs-content" data-tabs-content="product-types">
<div v-for="type, index in products.types" class="tabs-panel" :class="{ 'is-active': index == 0}" :id="type.name">
<p>Products</p>
<p>{{ index }} {{ type }}</p>
</div>
</div>
</div>
</section>
</template>
<script>
import { mapActions } from 'vuex';
import Header from './includes/Header.vue';
export default{
data() {
return {
products: {
types: []
}
}
},
components: {
'pos-header': Header,
},
mounted(){
let url = this.$store.getters.getApiUrl + '/product/types';
axios.get(url).then( response => {
this.products.types = response.data.productTypes;
$(document).foundation();
});
}
}
</script>
The mounted method fetches the data and saves them in a data property called products types and I have ascertained that the items are being retrieved. This application is contained within a laravel application and laravel handles the backend api.
I should also add that i am able to see the links that change the tab content but the contents of the tabs are not displayed.

vue.js 2.0 load component dynamically in laravel 5.3

Is it possible to load a component dynamically when clicking a list item in vuejs 2.0 and laravel 5.3? I have a standard Laravel app but on one section of the site (shows reports) I want to turn this page into a single page application using vuejs. I load the default report in using a vue component and all works well, like so:
<div class="col-md-3 col-sm-6">
<div class="card-box">
<h2>Reports</h2><br>
<ul>
<li>Daily</li>
<li>Weekly</li>
<li>Season</li>
<li>Label</li>
<li></li>
</ul>
</div>
</div>
<div class="col-md-9 col-sm-6">
<div class="card-box main">
<reports-homepage></reports-homepage>
</div>
</div>
What I'm trying to achieve is when one of the li items is clicked the <reports-homepage></reports-homepage> is changed dynamically according to which li is pressed. So it could become <reports-daily></reports-daily> for example.
I've tried putting a #click on the li component and catching that in a script below in the blade section but that gives me the following error:
[Vue warn]: Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <script>.
As pointed in the official documentation you can use dynamic components. In your case it could look something like this:
HTML
<div id="app">
<div class="col-md-3 col-sm-6">
<div class="card-box">
<h2>Reports</h2><br>
<ul>
<li #click="setComponent('daily')">Daily</li>
<li #click="setComponent('weekly')">Weekly</li>
<li #click="setComponent('season')">Season</li>
<li #click="setComponent('label')">Label</li>
</ul>
</div>
</div>
<div class="col-md-9 col-sm-6">
<div class="card-box main">
<component v-bind:is="currentComponent"></component>
</div>
</div>
</div>
JS
var vm = new Vue({
el: '#app',
data: {
currentComponent: 'daily'
},
components: {
'daily': { /* component object */ },
'weekly': { /* component object */ },
'season': { /* component object */ },
'label': { /* component object */ }
},
methods: {
setComponent: function (component) {
this.currentComponent = component;
}
}
})
And that should do what you are trying to achieve. Let me know if it helped!

Resources