Memory Leak vue axios - vue.js

I am building User Interface with Vue - created with #vue/cli 4.1.2.
Very Simple with not many components. Menu on the left side and results (in a table on the right side). Http requests are made with axios, and the results (responses) are approximately 1000 rows * 6 cols (cca 200KB sized objects). I am using Vuex and Router. However, the HEAP size keeps growing with every request I made. As if all the data downloaded(and much more) never gets released from memory. From initial 18MB heap size increases to cca100 MB with app 10 requests.
I tried to isolate the problem with turning off store, changing the template part (in fact, not showing anything at all but the number of records) but have no idea what is causing this. Tried to make values of gridData and gridColumns null before each reuest, but nothing helps. It seems that all the data get stored in memory and never release. Did try to analyze with google dev tools, but could not resolved that.
The component that is responsible for performing/and displaying requests is as follows:
<template>
<main>
<form id="search">Search <input
name="query"
v-model="searchQuery"
/></form>
<pre>cParamsRecord: {{ cParamsRecord }}</pre>
<pre>cActiveGrid: {{ cActiveGrid }}</pre>
<pre>cActiveRSet: {{ cActiveRSet }}</pre>
<div id="tbl">
<thead>
<tr>
<th
v-for="(col, index) in gridColumns"
:key="index"
>
{{ col }}
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(entry, index) in gridData"
:key="index"
#click="setActiveRow(entry)"
v-bind:class="{ active: entry == cActiveRow }"
>
<td
v-for="(col, index) in gridColumns"
:key="index"
>
{{ entry[col] }}
</td>
</tr>
</tbody>
<span>Num of records here: {{ propertyComputed }} </span>
</div>
</main>
</template>
<script>
import { rSetParams } from "#/playground/mockData.js";
import api_service from "#/services/api_service";
import * as types from "#/store/types.js";
export default {
name: "pGrid1",
components: {},
data () {
return {
searchQuery: "",
gridData: null,
gridColumns: null,
rSetParams: rSetParams,
property: "Blank"
};
},
computed: {
cActiveRSet: {
get () {
return this.$store.getters[types.ACTIVE_R_SET];
},
set (value) {
this.$store.commit(types.ACTIVE_R_SET, value);
}
},
cActiveGrid: {
get () {
return this.$store.getters[types.ACTIVE_GRID];
},
set (value) {
this.$store.commit(types.ACTIVE_GRID, value);
}
},
cRSetParams: {
get () {
return this.$store.getters[types.R_SET_PARAMS];
},
set (value) {
this.$store.commit(types.R_SET_PARAMS, value);
}
},
cActiveRow: {
get () {
return this.$store.getters[types.ACTIVE_ROW];
},
set (value) {
this.$store.commit(types.ACTIVE_ROW, value);
}
},
cParamsRecord: {
get () {
return this.$store.getters[types.PARAMS_RECORD];
},
set (value) {
this.$store.commit(types.PARAMS_RECORD, value);
}
},
},
methods: {
//function that makes http read http request and stores data locally into
//component data (this.gridData and this.gridColumns
async getRData () {
this.gridData = null
this.gridColumns = null
console.log(
"starting getRData for grid: ",
this.cActiveGrid,
" and routine set: " + this.cActiveRSet
);
if (this.cActiveRSet && this.cActiveGrid) {
var queryData;
try {
this.$store.commit(types.IS_LOADING, true);
const routine = this.cActiveRSet + "_" + this.cActiveGrid + "r";
console.log("routine: ", routine);
const r1 = await api_service.getRData(routine);
//const result = await r1
queryData = r1;
this.gridData = queryData.data;
console.log(this.gridData);
this.gridColumns = this.getTblHeadersFromResults(this.gridData);
//store data into the right vuex variable (in this case R1_DATA)
if (this.cActiveGrid == 1) {
this.$store.commit(types.R1_DATA, queryData.data);
} else if (this.cActiveGrid == 2) {
this.$store.commit(types.R2_DATA, queryData.data);
} else if (this.cActiveGrid == 3) {
this.$store.commit(types.R3_DATA, queryData.data);
}
this.$store.commit(types.IS_LOADING, false);
return queryData;
} catch (e) {
this.$store.commit(types.IS_LOADING, false);
console.error(e);
}
} else {
console.log(
"async getRData not run because there's no cActiveRSet or no cActiveGrid"
);
}
},
//function for defining active row in a table - - when we click on the row, row object get stored into cActiveRow
setActiveRow (row) {
console.log("in setActiveRow and row is: ", row);
this.$store.commit(types.ACTIVE_ROW, row);
this.createParamsRecordForNextGrid();
this.getRDataForNextGrid();
},
//creating tblHeaders object from first line of results
getTblHeadersFromResults (res) {
var tblHeadersRaw = Object.keys(res[0]);
return tblHeadersRaw;
},
//combining values from our record(clicked) - iecActiveRow with rParams to get appropriate record for using it when making http request
createParamsRecordForNextGrid () {
console.log("starting func createParamsRecord");
var nextGrid = this.cActiveGrid + 1;
var rR = this.cActiveRSet + "_" + nextGrid.toString() + "r"; //creating the name of our routine here
//const rR = "r_00305"
console.log("rR: ", rR);
//console.log("rSetParams: " + JSON.stringify(rSetParams))
var rParams = this.cRSetParams.filter(r => r.i9_routine_name == rR); //onyl get the params for our routine from i9 (rSetParams)
console.log("rParams: ", rParams);
// eslint-disable-next-line no-unused-vars
var paramsRecord = {};
console.log(this.cActiveRow);
var cActiveRowObj = this.cActiveRow;
for (var key in cActiveRowObj) {
//console.log(key)
for (var i = 0; i < rParams.length; i++) {
var rParamsObj = rParams[i];
//console.log("rParamsObj.i9_header_db: ", rParamsObj.i9_header_db, " ************** key from cActiveRow: ", key)
if ("p_" + key == rParamsObj.i9_header_db) {
//console.log("matching key with rParamsObj: ", key, rParamsObj.i9_header_db)
//console.log("value: ", cActiveRowObj[key])
paramsRecord[rParamsObj.i9_header_db] = cActiveRowObj[key];
}
}
}
this.$store.commit(types.PARAMS_RECORD, paramsRecord);
return paramsRecord;
}
//create types String - based on cActiveGrid we create types string so that we can store table data into the right variable in this.$store
},
watch: {
cActiveRSet: {
// eslint-disable-next-line no-unused-vars
handler () {
this.getRData();
},
immediate: true
}
}
};
</script>
After

Related

Virtual Image Path not visible in partial View

bit of a weird one here. I tried finding an answer, but was unable to. New to node.
Problem: My virtual image paths work in my views, but not in my partial view, being the navbar. This navbar has a searchbar, and it is fetching the succulent plants in the db, with the following code:
let searchBar = document.getElementById("searchBar");
searchBar.addEventListener("keyup", searchDatabase);
function searchDatabase() {
const searchResults = document.getElementById("searchResults");
//Reg expressions prevent special characters and only spaces fetching from db
let match = searchBar.value.match(/^[a-zA-Z ]*/);
let match2 = searchBar.value.match(/\s*/);
if (match2[0] === searchBar.value) {
searchResults.innerHTML = "";
return;
}
if (match[0] === searchBar.value) {
fetch("searchSucculents", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ payload: searchBar.value }),
})
.then((res) => res.json())
.then((data) => {
let payload = data.payload;
searchResults.innerHTML = "";
if (payload.length < 1) {
searchResults.innerHTML = "<p>No search results found</p>";
return;
} else if (searchBar.value === "") {
searchResults.innerHTML = "";
return;
} else {
payload.forEach((item, index) => {
if (index > 0) {
searchResults.innerHTML += "<hr>";
}
searchResults.innerHTML +=
`<div class="card" style="width: 18rem;">
<img src="${item.SucculentImagePath}" class="card-img-top" alt="${item.SucculentName}">
<div class="card-body">
<p class="card-text">${item.SucculentName}</p>
</div>
</div>`;
});
}
return;
});
}
searchResults.innerHTML = "";
}
Here is the route:
app.post("/searchSucculents", async (req, res) => {
let payload = req.body.payload.trim();
let search = await Succulent.find({SucculentName: {$regex: new RegExp("^"+payload+".*","i")}}).exec();
//Limit Search Results to 10
search = search.slice(0, 10);
res.send({payload: search});
})
Here's the part in my schema defining the image path:
succulentSchema.virtual('SucculentImagePath').get(function() {
if (this.SucculentImage != null && this.SucculentImageType != null) {
return `data:${this.SucculentImageType};charset=utf-8;base64,${this.SucculentImage.toString('base64')}`
}
})
I'm able to reference this image path in my full views, as follows:
<img src="<%= succulent.SucculentImagePath %>">
However, when I try to access the SucculentImagePath attribute in this searchbar in my nav, it is undefined. Am I missing something here?
After doing further research, I discovered that you cant use mongoose virtuals when parsing data to JSON (noob mistake, I know).
I fixed it, by adding this as an option in the schema object:
toJSON: { virtuals: true } })

Vue js - not all the data showing after store dispatch

The select box not showing sometimes the first color and sometimes not showing the first color.
How can i make it to show all the item in the select box?
I'm not getting for some reason all the promises
You can see the issue in the picture
Please help me to fix this issue i'm new to vue js
My code:
data() {
return {
propertiesTree: []
}
}
getPropertyGroups(objectId: number): void {
if (this.$data.currentObjectId === objectId)
return;
let component: any = this;
this.$data.currentObjectId = objectId;
component.showLoader();
this.$store.dispatch('properties/getPropertyGroups', objectId)
.then(({ data, status }: { data: string | Array<propertiesInterfaces.PropertyGroupDto>, status: number }) => {
// console.log(data, "data");
// console.log(status, "status")
if (status === 500) {
this.$message({
message: data as string,
type: "error"
});
}
else {
let anyData = data as any;
anyData.map(item => {
item.properties.map(prop => {
if(prop.type.toString() === 'dictionary'){
prop.dictionaryList = [];
prop.color = '';
this.getWholeDictionaryList(prop.dictionaryId.value, prop)
}
});
});
}
component.hideLoader();
});
},
getWholeDictionaryList(dictionaryId: number, prop: any){
this.$store.dispatch('dictionaries/getWholeDictionaryList', dictionaryId).then(
({ data, status }: { data: Array<any> |string , status: number }) => {
if (status === 500) {
this.$message({
message: data as string,
type: "error"
});
} else {
const arrData = data as Array<any>;
arrData.map((item,index) => {
prop.dictionaryList = [];
prop.dictionaryList = data;
this.getDictionaryItemColor(item.id.value, data, index, prop);
});
}
});
},
getDictionaryItemColor(dictionaryItemId:number, dictionaryList: Array<any>, index:number, current){
this.$store.dispatch('patterns/getDictionaryItemColor', dictionaryItemId).then((data: any, status: number) => {
if (status === 500) {
this.$message({
message: data as string,
type: "error"
});
} else{
debugger
if(current.dictionaryItemId.value === data.data.sceneObjectId)
current.color = data.data.colorString;
dictionaryList[index].color = data.data.colorString ? data.data.colorString: '#FFFFFF';
}
});
},
Html code of the select box
<el-select v-model="data.color" placeholder="Select">
<el-option
v-for="item in data.dictionaryList"
:key="item.name"
:label="item.color"
:value="item.color">
</el-option>
</el-select>
I did return to dispatch
let dispatch = this.getWholeDictionaryList(prop.dictionaryId.value, prop)
let promiseArr = [];
promiseArr.push(dispatch);
after the map closing tag i did
Promise.all(promisArr).then( () => {
debugger
this.$data.propertiesTree = anyData;
});
And I've got it solved

input file component is not updating, VueJS

I have some code where I update multiple files using a package.
Add / Remove seems to work if I console.log, but if I do a POST request, on server I get all files, even if I delete them.
Example: I add 3 files, I delete 2 of them and I do a POST, on server I get 3 files. (But on console.log it shows me that I have only 1 which is correct).
Also, I find this article , but I am not sure what to do in my case.
This is a short version of my code.
<div id="upload-files-on-update">
<file-upload
:multiple="true"
v-model="certifications"
input-id="certifications"
name="certifications[]"
#input-filter="inputFilter"
ref="upload">
<span class="button">Select files</span>
</file-upload>
</div>
new Vue({
el: '#upload-files-on-update',
data: function () {
return {
certifications: [],
}
},
components: {
FileUpload: VueUploadComponent
},
methods: {
updateFiles(){
let formData = new FormData();
this.certifications.forEach((file, index) => {
if (!file.status && file.blob) {
formData.append("certifications[]",
{
types: this.accept,
certifications_ids: this.certifications_ids,
}
);
this.loadingButton = true;
}
});
axios
.post("<?php echo $link;?>", formData, {
headers: {
"Content-Type": "multipart/form-data"
},
params:{
types: this.accept,
certifications_ids: this.certifications_ids,
}
})
},
inputFilter(newFile, oldFile, prevent) {
if (newFile && !oldFile) {
if (/(\/|^)(Thumbs\.db|desktop\.ini|\..+)$/.test(newFile.name)) {
return prevent()
}
if (/\.(php5?|html?|jsx?)$/i.test(newFile.name)) {
return prevent()
}
}
if (newFile && (!oldFile || newFile.file !== oldFile.file)) {
newFile.blob = ''
let URL = window.URL || window.webkitURL
if (URL && URL.createObjectURL) {
newFile.blob = URL.createObjectURL(newFile.file)
}
newFile.pending = true;
newFile.thumb = ''
if (newFile.blob && newFile.type.substr(0, 6) === 'image/') {
newFile.thumb = newFile.blob
}
}
},
// Remove file from table
removeFile(index) {
this.certifications.splice(index, 1);
},
}
});
I found a solution for this problem.
//I catch ajax request and I make sure that is the request that I want it
var self = this;
$.ajaxSetup({
beforeSend: function (xhr,settings) {
if(settings.type != 'POST'){
return ;
}
if(settings.data.get('controller') != 'wcfm-memberships-registration'){
return ;
}
// Here I set file input as an empty array
settings.data.set('certifications[]',[]);
// Here I add my new files from a VueJS array
self.certifications.forEach((file, index) => {
settings.data.append("certifications[]", file.file);
});
}
});
});

Change value after confirmation in input VUE-JS

I have a table with an input column. This input column will have value if it has been saved in ddbb before.
If this value changes, handled with an event '#blur', I show a modal to confirm the change.
My problem is that if you want to keep the old value it will always change...
I tried to change this value with javascript, but it doesn't work... Any suggestions?
This is my code:
<b-tbody>
<b-tr
v-for="(driver, index) in drivers"
:key="driver.clientId">
<b-td
data-label="Client"
:title="driver.clientName">{{ driver.clientName }}</b-td>
<b-td data-label="Numerator">
<b-input
:id="'numeratorDriver_'+index"
class="driver-numerator text-right"
type="text"
:value="(driver.driverNumerator === undefined) ? 0 : $utils.formatNumber(driver.driverNumerator)"
#blur="calculatePricingDriver($event, index)" /></b-td>
<b-td
class="text-right"
data-label="Pricing"
:title="driver.pricingDriver"><span>{{ driver.pricingDriver }}</span></b-td>
</b-tr>
</b-tbody>
<script>
function calculatePricingCustomDriver (event, index) {
let lastValue = this.drivers[index].driverNumerator
if (this.$store.getters.price !== undefined) {
let title = 'Modify numerator'
let message = 'If you modify numerator driver, the calculated pricing will be deleted'
this.$bvModal.msgBoxConfirm(message, {
title: title,
size: 'sm',
buttonSize: 'sm',
okTitle: 'Yes',
okVariant: 'primary',
cancelTitle: 'No',
cancelVariant: 'primary',
hideHeaderClose: false,
centered: true
})
.then(confirmed => {
if (confirmed) {
this.$http.get('delete-price', { params: { id: this.$store.getters.id } })
.then((response) => {
if (response.status === this.$constants.RESPONSE_STATUS_OK) {
this.price = ''
let newNumerator = event.target.value
this.drivers[index].driverNumerator = Number(newNumerator)
let sumTotal = _.sumBy(this.drivers, 'driverNumerator')
for (let i = 0; i < this.drivers.length; i++) {
this.drivers[i].pricingDriver = (this.drivers[i].driverNumerator / sumTotal).toFixed(2)
}
} else {
this.drivers[index].driverNumerator = lastValue
// this is that I want change because it doesn't work fine
document.getElementById('numeratorDriver_' + index).value = lastValue
}
})
} else {
this.drivers[index].driverNumerator = lastValue
document.getElementById('numeratorDriver_' + index).value = lastValue
}
})
.catch(() => {
/* Reset the value in case of an error */
this.$utils.showModalError()
})
} else {
let newNumerator = event.target.value
this.drivers[index].driverNumerator = Number(newNumerator)
let sumTotal = _.sumBy(this.drivers, 'driverNumerator')
for (let i = 0; i < this.drivers.length; i++) {
this.drivers[i].pricingDriver = (this.drivers[i].driverNumerator / sumTotal).toFixed(2)
}
}
}
</script>
how is calculatePricingCustomDriver being loaded into your Vue component? For it to be called like that from #blur you would need to define it as a method:
<template>
<!-- your table-->
</template>
<script>
export default {
name: "MyComponent",
methods : {
calculatePricingCustomDriver () {
// your code
}
}
}
</script>
Or it could be installed as a global mixin

VueJS2 v-html with filter

How to display raw html with filter?
I have something like this:
K.json = function( json ) {
if( typeof json!='string' ) json = JSON.stringify( json, null, 2 );
json = json.replace( /</g, '<' ).replace( />/g, '>' ); // replace(/&/g, '&')
var pattern = /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g;
var html = json.replace( pattern, function( match ) {
var cls = 'number';
var suffix = '';
if( /^"/.test( match ) ) {
if( /:$/.test( match ) ) {
cls = 'key';
match = match.slice( 0, -1 );
suffix = ':'
} else {
cls = 'string';
}
} else if( /true|false/.test( match ) ) {
cls = 'boolean';
} else if( /null/.test( match ) ) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>' + suffix;
} );
return html;
};
Vue.filter( 'json', K.json );
And use them something like this:
<div v-html="thecolumn | json"></div>
It shows a warning and displayed incorrectly:
vue.js:523 [Vue warn]: Property or method "json" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
(found in root instance)
I also tried solution from forum: https://laracasts.com/discuss/channels/vue/use-a-filter-custom-filter-in-v-html-property?page=1
<p v-html="this.$options.filters.json(description)"></p>
It shows error:
[Vue warn]: Error when rendering root instance:
vue.js:3063 Uncaught TypeError: Cannot read property 'filters' of undefined
at eval (eval at makeFunction (vue.js:8260), <anonymous>:2:2975)
at Proxy.renderList (vue.js:3158)
at Proxy.eval (eval at makeFunction (vue.js:8260), <anonymous>:2:2169)
at Vue$3.Vue._render (vue.js:3054)
at Vue$3.<anonymous> (vue.js:2430)
at Watcher.get (vue.js:1661)
at new Watcher (vue.js:1653)
at Vue$3.Vue._mount (vue.js:2429)
at Vue$3.$mount (vue.js:6000)
at Vue$3.$mount (vue.js:8327)
What's the correct way to do this on VueJS2?
For completeness, in Vue 2 you have some options, like:
v-html="$options.filters.FILTERNAME(args)" or
:inner-html.prop="args | FILTERNAME" or
v-html="METHODNAME(args)", if you create a method.
For Vue 3, filters are no longer supported. So the only options are method (or a computed prop).
More info in the demos below.
Vue 2:
function json(text) {
// do your magic
return text.toUpperCase(); // just for demo
}
Vue.filter('json', function (value) {
return json(value);
})
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods: {
jsonMethod(v) {
return json(v); // create a "proxy" to the outer function
}
}
})
<script src="https://unpkg.com/vue#2"></script>
<div id="app">
<p v-html="$options.filters.json(message)"></p>
<p :inner-html.prop="message | json"></p>
<p v-html="jsonMethod(message)"></p>
</div>
Vue 3:
In Vue.js 3 filters are no longer supported. Therefore the only options left is to use a method or a computed property:
function json(text) {
// do your magic
return text.toUpperCase(); // just for demo
}
const { createApp } = Vue
createApp({
data() {
return {
message: 'Hello Vue.js!'
}
},
methods: {
jsonMethod(v) {
return json(v); // create a "proxy" to the outer function
}
},
computed: {
transformedMessage() {
return json(this.message)
}
}
}).mount('#app')
<script src="https://unpkg.com/vue#3/dist/vue.global.js"></script>
<div id="app">
<p v-html="jsonMethod(message)"></p>
<p v-html="transformedMessage"></p>
</div>
For me, as #acdcjunior pointed out, this worked:
<p v-html="$options.filters.json(description)"></p>
Besides that, I had two filters to apply, and it also worked:
<p v-html="$options.filters.filter1($options.filters.filter2(description))"></p>
The issue is that your HTML is processed before the filter is added to your Vue instance. Try like this:
JSFiddle
var jsonFormatter = function(json){
if( typeof json!='string' ) json = JSON.stringify( json, null, 2 );
json = json.replace( /</g, '<' ).replace( />/g, '>' ); // replace(/&/g, '&')
var pattern = /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g;
var html = json.replace( pattern, function( match ) {
var cls = 'number';
var suffix = '';
if( /^"/.test( match ) ) {
if( /:$/.test( match ) ) {
cls = 'key';
match = match.slice( 0, -1 );
suffix = ':'
} else {
cls = 'string';
}
} else if( /true|false/.test( match ) ) {
cls = 'boolean';
} else if( /null/.test( match ) ) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>' + suffix;
} );
return html;
}
new Vue({
el: '#app',
data(){
return {
jsonData: {dog: 'woof', nestedObject: {cat: 'meow'}}
}
},
filters: {
jsonFormatter: jsonFormatter
}
});
//Vue.filter( 'jsonFormatter', jsonFormatter ); // Doesn't work becuase happens after html is processed
<div id="app" class="container">
<h1>
v-html directive
</h1>
<div v-html="this.$options.filters.jsonFormatter(jsonData)"></div>
</div>
If you're just displaying the data then create a method:
json(jsonable) {
return jsonedValue;
}
then in the html
<div v-html="json(mydata)"></div>