Typing lag using Vue autosave with debounce to Firestore - vue.js

Mounted:
mounted() {
this.$fire.firestore
.collection("blueprints")
.doc(this.id)
.get()
.then((snapshot) => {
this.blueprint = snapshot.data();
})
.catch((err) => {
console.log(err, "Error here.");
});
},
Created:
created() {
this.updateBlueprint = _.debounce(this.updateBlueprint, 2000);
},
Method:
async updateBlueprint() {
await this.$fire.firestore
.collection("blueprints")
.doc(this.id)
.update(this.blueprint)
.then((res) => {
// Success
})
.catch((err) => {
console.error("Error updating: ", err);
});
},
Template:
...
<Textarea
v-model="blueprint.description"
#input="updateBlueprint"
/>
...
Component:
<template>
<div class="textarea">
<textarea
v-model="currentValue"
v-bind="$attrs"
ref="input"
v-autogrow
/>
</div>
</template>
Any idea on why there's such a lag? It's fine for the first couple minutes as I type, but the more I type and the more data I add, the slower and slower it types. At some points, Chrome has asked me if I want to kill the page or wait until its ready. Not sure what's causing the problem here, any suggestions? This is on my local server.
Update: I did discover that when running update, when I type into the textarea, it updates the entire array instead of individual parts of the array. My data is set up like this:
{
title: '...',
description: '...',
track: [
{
title: '...',
description: '...'
},
{
title: '...',
description: '...'
},
{
title: '...',
description: '...'
},
{
title: '...',
description: '...'
},
{
title: '...',
description: '...'
}
]
Instead of updating individual items in the track array, its updating the entire track array, could that be causing the lag?

Related

Filter data coming from API with Nuxt

Im using slick slider in Nuxt im getting the images from an API with Axios
The API look like this :
"images_selection": [
{
title: 'Global Landing',
slug: 'global-landing-1',
id: 113,
images: [
{
title: '',
description: '',
file_extension: 'jpeg',
position: 0,
url: 'https://####images',
type: 0,
link: '',
id: 3603,
video_link: '',
},
],
},
{
title: 'Home Slider 1',
slug: 'home-slider-1',
id: 331,
images: [
{
title: '',
description: '',
file_extension: 'jpeg',
position: 0,
url: 'https://###images',
type: 0,
link: '',
id: 5773,
},
],
},
]
My Axios :
async mounted() {
const response = await axios.get('/api/')
this.resultsimages = response.data.images_selection.filter(r => r.slug = home-slider-1)
},
Im trying to get the image only in "Home Slider 1" with a filter .filter(r => r.slug = home-slider-1);
But im doing something wrong what will be the best way to target only the home slider 1 ?
EDIT: here is my page where I am not able to loop on the fetched images.
<template>
<div>
<div class="home-slider">
<VueSlickCarousel
:arrows="false"
:dots="true"
:slidesToShow="3"
:rows="1"
:slidesPerRow="3"
:autoplay="true"
:fade="true"
>
<div class="slide1">
<div v-for="result in resultsimages" :key="result.id">
<div class="img1">
<img
v-for="images in result.images"
:key="images.id"
:src="images.url"
/>
</div>
</div>
</div>
</VueSlickCarousel>
</div>
</div>
</template>
<script>
export default {
data() {
return {
resultsimages: [],
data: [],
};
async mounted() {
const { data } = await axios.get("/api/", {
headers: {
"X-AUTH-TOKEN": "####",
"Content-Type": "application/json",
},
});
this.resultsimages = data.images_selection.filter((image) => (image) =>
image.slug === "home-slider-1"
);
},
};
</script>
I heavily formatted your question, especially for the axios part by using only async/await and not a mix of both async/await + .then.
This is how the block should look like
async mounted() {
const { data } = await axios.get('/api/')
this.resultsimages = data.images_selection.filter((image) => (image) => image.slug === 'home-slider-1')
},
Please format your code with more effort next time.

datatable vueJS don´t show data

I´m trayin integrate datatable in my component vue. For this i´m using this library:
https://jamesdordoy.github.io/
i´m install in my proyect, i has configured it, created my component and include in my view, but returned me this message:
Invalid prop: type check failed for prop "data". Expected Object, got Array
my component it´s:
<template>
<div>
<div class="row justify-content-center w-100">
<data-table :data="data" :columns="columns" #on-table-props-changed="reloadTable"></data-table>
</div>
</div>
</template>
<script>
export default {
data() {
return {
url: "/admin/vue/getAllUsers",
data: {},
tableProps: {
search: '',
length: 10,
column: 'id',
dir: 'asc'
},
columns: [
{
label: 'ID',
name: 'id',
orderable: true,
},
{
label: 'Name',
name: 'name',
orderable: true,
},
{
label: 'Email',
name: 'email',
orderable: true,
},
]
}
},
created() {
this.cargar(this.url);
},
methods:{
cargar(url = this.url, options = this.tableProps){
axios.get(url, { params: options })
.then((response) => {
this.data = response.data;
})
.catch((error) => console.error(error));
},
reloadTable(tableProps) {
this.cargar(this.url, tableProps);
}
},
}
</script>
i was trayed with response.data[0] the error disappear but my table it´s empty, i don´t know that i´m doing wrong.
In my controller i have this:
return User::all();
i´m working with laravel 8
thanks for help
you should pass props :items = "data" instead of :data = "data"

Vue - Pass params from select inside an array.push

When passing params from a select, the usual syntax would be something like this.
<v-select
v-model="item_id"
items="items"
item-text="item_name"
item-value="id"
#change='itemDescription()'
/>
export default {
data: ()=> ({
item_id: '',
items: [],
itemDescription '',
}),
method: {
itemDescription() {
axios.get('/api/item', {
params: { id: this.item_id }
})
.then(response => this.itemDescription = response.data )
}
}
}
Now the problem comes when the select is inside an array.push. I can't simply pass the value to this.item_id since the structure would be object[0][1]. Here is an example syntax below.
<v-btn #click="addItem()">Add</v-btn>
<template v-for="item in items" :key="item.id>
<v-select
v-model="item.item_id"
items="item_all"
item-text="item_name"
item-value="id"
#change='itemDescription()'
/>
</template>
export default {
data: ()=> ({
items: [],
item_all: '',
}),
method: {
addItem() {
this.items.push({
item_id: '',
description: '',
}),
itemDescription() {
axios.get('/api/item', {
params: { id: this.items.item_id } // <-- this doesn't work
})
.then(response => this.items.description = response.data )
}
}
}
How do I pass id of each row every time I select each one of them?
I made it work now. I just used index, this was my savior.
Please see code below for those interested
<v-btn #click="addItem()">Add</v-btn>
<template v-for="(item, index) in items" :key="item.id>
<v-select
v-model="item.item_id"
items="item_all"
item-text="item_name"
item-value="id"
#change='itemDescription(index)'
/>
</template>
export default {
data: ()=> ({
items: [],
item_all: '',
itemDescription '',
}),
method: {
addItem() {
this.items.push({
item_id: '',
description: '',
}),
itemDescription(index) {
axios.get('/api/item', {
params: { id: this.items[index].item_id } // <-- this doesn't work
})
.then(response => this.items[index].description = response.data )
}
}
}
The 'this' in itemDescription method isn't same as 'this' in 'method:'.
so it will not work.
use another variable to keep this, and use it in itemDescription.
method: {
var that = this;
addItem() {
this.items.push({
item_id: '',
description: '',
}),
itemDescription() {
axios.get('/api/item', {
params: { id: that.items.item_id } // 'this' ain't work
})
.then(response => this.items.description = response.data )
}

Make computations in a child component after axios requests in a parent component

Here is my problem : I have a parent component Edit that makes several axios requests, the results of these requests are passed down to a child component (Menu in my example).
<template>
<div>
<p>
Order Id : {{ this.$route.params.id }}
</p>
<head-fields :menu="menu"></head-fields>
<div>
<b-tabs content-class="mt-3">
<b-tab title="Menu" active>
<Menu :menuItems="items" :nutritionalValues="nutritionalValues"></Menu>
</b-tab>
<b-tab title="Specifications">
<specifications :specifications="specifications">
</specifications>
</b-tab>
<b-tab title="Redundancies"><p>I'm the redundancies tab!</p></b-tab>
</b-tabs>
</div>
</div>
</template>
<script>
import HeadFields from "./HeadFields";
import Menu from "./Menu";
import Specifications from "./Specifications";
export default {
name: "Edit",
components: {HeadFields, Menu, Specifications},
data(){
return{
menu: {},
loaded: false,
items: {},
nutritionalValues: {},
specifications: {},
error:{}
}
},
created(){
this.find(this.$route.params.id);
},
methods:{
find(id){
axios.get('/menusV2/'+id)
.then(response => {
this.loading = false;
this.menu = response.data[0];
this.fetchMenu(this.menu.orderId);
this.fetchSpecifications(this.menu.orderId);
});
return this.menu;
},
fetchMenu(orderId){
// console.log(orderId);
axios
.get('/menusV2/'+orderId+'/menu')
.then(response => {
this.loading = false;
this.items = response.data.items;
this.nutritionalValues = response.data.nutritionalValues;
})
.catch(error => {
this.loading = false;
this.error = error.response.data.message || error.message;
})
},
fetchSpecifications(orderId){
axios
.get('/menusV2/'+orderId+'/specifications')
.then(response => {
this.loading = false;
this.specifications = response.data;
// this.checkSpecifications();
})
.catch(error => {
this.loading = false;
// this.error = error.response.data.message || error.message;
})
}
}
}
</script>
The data is passed down to the child component "Menu" as a prop :
<template>
<div class="panel panel-default">
<b-table
striped hover
:items="menuItems"
:fields="fields"
:primary-key="menuItems.pivotId"
>
</b-table>
</div>
</template>
<script>
export default {
name: "Menu",
props: ['menuItems', 'nutritionalValues'],
data() {
return {
loading: true,
perPage: ['10', '25', '50'],
rowSelected: true,
fields: [
{key: "meal", label: "Meal", sortable: true},
{key: "category", label: "Category", sortable: true},
{key: "itemName", label: "Name", sortable: true},
{key: "noOfServing", label: "Serving", sortable: true},
{key: "weight", label: "Weight", sortable: true},
{key: "calories", label: "Calories", sortable: true},
{key: "carbs", label: "Carbs", sortable: true},
{key: "proteins", label: "Proteins", sortable: true},
{key: "fats", label: "Fats", sortable: true},
]
}
},
mounted(){
this.checkSpecifications();
},
methods:{
searchIngredientSpecification(itemId, itemName, specifications){
//Checking of the ingredients name
for (var i=0; i < specifications.length; i++) {
if (specifications[i].itemName === itemName) {
console.log("Specification ! "+itemName);
}
}
//Checking of the nutritional properties
var ingredientNutritionalProperties = {};
axios
.get('/menusV2/'+itemId+'/ingredient/nutritionalProperties')
.then(response => {
ingredientNutritionalProperties = response.data;
});
console.log("Ingredient : " + itemName);
console.log(ingredientNutritionalProperties);
},
searchDishSpecification(itemId, itemName, specifications){
//Checking of the ingredients name
for (var i=0; i < specifications.length; i++) {
if (specifications[i].itemName === itemName) {
console.log("Specification ! "+itemName);
}
}
//Checking of the nutritional properties
var dishNutritionalProperties = {};
axios
.get('/menusV2/'+itemId+'/dish/nutritionalProperties')
.then(response => {
dishNutritionalProperties = response.data;
});
console.log("Dish : " + itemName);
console.log(dishNutritionalProperties);
var ingredientsDish = {};
var ingredientsNutritionalProperties = {};
axios
.get('/menusV2/'+itemId+'/getIngredients')
.then(response => {
ingredientsDish = response.data.ingredients;
ingredientsNutritionalProperties = response.data.nutritionalProperties;
});
console.log("Dish : " + itemName);
console.log(ingredientsDish);
console.log(ingredientsNutritionalProperties);
},
checkSpecifications(){
console.log("Check Specifications launched !");
console.log(this.menuItems);
var items = this.menuItems;
items.forEach(
element => {
switch(element.type){
case 'Ingredient':
this.searchIngredientSpecification(element.itemId,element.itemName,this.specifications);
break;
case 'Dish':
this.searchDishSpecification(element.itemId,element.itemName,this.specifications);
break;
}
}
);
},
}
}
</script>
The problem I have is around the methods in the child component that are fired before the menuItems prop is filled with data from the axios request.
I think that a possible fix to this problem would be to use computed properties or watchers but I don't really know if it will help me..
Here is the error that is thrown :
Thanks for your help !
You are getting the error because when the checkSpecifications method is run on mount, this.menuItems is not an array and forEach must only be used on arrays.
One option is to add a watcher to menuItems and only once it has been filled with a value (making sure it's an array) then run the checkSpecifications method.
Alternatively, you could define menuItems as an array and provide a default value.
props: {
menuItems: {
type: Array,
default: []
},
nutritionalValues: {
type: Array,
default: []
}
It's always good practice to define the type of your props.

Vue-Slick and v-for with dynamic data

i'm using vue-slick to show my images..
i've tried every solution that i found.. but none is working.
here is my template:
<slick ref="slick" :options="slickOptions">
<img v-for="(item) in categories" :src="'/images/category/'+item.image_url" alt="" class="img-fluid" >
</slick>
and here is my scripts:
data () {
return {
categories:'',
slickOptions: {
dots: true,
infinite: false,
autoplay: false,
arrows : false,
draggable:true,
speed: 1000,
slidesToShow: 1,
slidesToScroll: 1,
},
}
},
mounted() {
let _this = this;
axios({
method: 'post',
url: '/api/category',
data : {'name' : _this.name}
}).then( (response)=> {
console.log(response.data.data);
_this.categories = response.data.data;
}).catch((error) => {
console.log(error.response)
});
},
methods:{
next() {
this.$refs.slick.next();
},
prev() {
this.$refs.slick.prev();
},
reInit() {
this.$refs.slick.reSlick()
}
},
and only loading the image, and the slick is not working...!!?
I have faced the same issue, and what I did to solve this is to put the
v-if="categories.length > 0" on the <slick> tag.
It make the slick won't be created before the data that we want to display contains the data first.
Use below code to reinit slick, and call on success function of response
reInit() {
let currIndex = this.$refs.slick.currentSlide()
this.$refs.slick.destroy()
this.$nextTick(() => {
this.$refs.slick.create()
this.$refs.slick.goTo(currIndex, true)
})
}
I'm assuming your Axios is returning data with the structure you are looking for.
I'm also assuming you are using the vue-slick component and not slick.
You should iterate through a DIV like stated in the documentation. Without Axios, I did this:
In template:
<slick ref="slick" :options="slickOptions">
<div>Escolhe uma configuração...</div>
<div v-for="d in data1"><a class="inline" :href="d.image"><img :src="d.image" alt="">{{ d.text }}</a></div>
</slick>
In Javascript:
data: function() {
return {
data1: [
{ image: 'http://placehold.it/100x100', text: 'Config1' },
{ image: 'http://placehold.it/100x100', text: 'Config2' },
{ image: 'http://placehold.it/100x100', text: 'Config3' },
{ image: 'http://placehold.it/100x100', text: 'Config4' }
]
}