'This' undefined in parsing the xml API using react - javascript

I have been trying to make a data from XML clickable in react. This is working in one of the function but gives an error when it is called in an another function.
Shows = Unhandled Rejection (TypeError): Cannot read property 'getAuthArticles' of undefined
I have tried using bind(this). I am already using the arrow functions
getData() {
fetch(`http://export.arxiv.org/api/query?search_query=ti:${'therapy'}&sortBy=lastUpdatedDate&sortOrder=ascending`).then(data=>data.text()).then(str => (new window.DOMParser()).parseFromString(str, "text/xml")).then(data => {
var entry = data.getElementsByTagName("entry");
let elems = []
for(let i=0;i<entry.length;i++){
console.log(entry);
let elem = <div key={i} id={entry[i].getElementsByTagName("id")[0].textContent} onClick={this.handleChange}>{entry[i].getElementsByTagName("title")[0].textContent}</div>;
elems.push(elem);
}
console.log(elems)
this.setState({data: elems});
})
}
/* */
handleChange(evt) {
console.log(evt.target.id)
var res = evt.target.id.split("/");
var id = res[5]
fetch(`http://export.arxiv.org/api/query?id_list=${res[4]}/${id}`)
.then(data=>data.text()).then(str => (new window.DOMParser()).parseFromString(str, "text/xml"))
.then(data => {
var summ = data.getElementsByTagName("summary");
let auth = data.getElementsByTagName("author");
let elems1 = [];
let name = [];
console.log(auth)
for (let i = 0; i < auth.length; i++ ){
console.log(auth[i].textContent);
console.log(auth[i].getElementsByTagName("id"));
let elem1 = <div key={i} id={auth[i].textContent} onClick={this.getAuthArticles.bind(this)}>{auth[i].textContent}</div>;
name += auth[i].textContent;
// elems1.push(elem1);
// console.log(elem1)
}
document.getElementById("demo").innerHTML = summ[0].textContent + name;
// window.history.pushState({}, "page 2", "bar.html");
// this.setState({data: elems1});
})
}
getAuthArticles(evt) {
console.log(evt.target.id)
let auth_name = evt;
fetch(`http://export.arxiv.org/api/query?search_query=${auth_name}`)
.then(data=> data.text()).then(str => (new window.DOMParser()).parseFromString(str, "text/xml"))
.then(data => {
var arti = data.getElementsByTagName("title");
let titles = []
for(let i=0;i<arti.length;i++){
let elem = <div key={i} id={arti[i].getElementsByTagName("id")[0].textContent}>{arti[i].getElementsByTagName("title")[0].textContent}</div>;
titles.push(elem);
}
console.log(titles)
})
}

I see you're using normal function and you missing bind(this) in getData()
onClick={this.handleChange}
// should change to
onClick={this.handleChange.bind(this)}
And make sure you bind(this) for getData()
It will easier with arrow function like
handleChange = (evt) => {}
you don't need to bind(this) for arrow function

Related

value of queryselected/getElementByClass returns value as empty

when I try to querySelect an Element from another module, it always returns empty string ? what should do to fix this? the element exists because when i console log it, the element is there, just the value always returns empty even after i have filled the input in UI, its just that when i press submit its returning empty.
furthermore, if i move the code to the scope where the function is being exported, it works
below code is inside the function footerSubmitSection() that is being exported to another module called addNewOrderFormMain.js where all the components is being appended to the main container and exported to the index.js
the console.log returns
// order ID
db.collection('UserDatabase').doc(currentUserID).get().then((doc) => {
function dateForOrder(){
let today = new Date();
let dd = String(today.getDate()).padStart(2, '0');
let mm = String(today.getMonth() + 1).padStart(2, '0');
let yyyy = today.getFullYear();
today = mm+dd+yyyy;
return today
}
let todayOrderNum = db.collection('OrderDatabase').where('OrderDate', '==', `${orderDate}`)
.get()
.then((snapshot) => {
length = snapshot.size + 1;
let salesRepID = doc.data().SaleID
let salesRepName = doc.data().Name
let orderID = salesRepID + dateForOrder()+'-'+ length + "-"+ editversion
console.log(deliveryMethodInput)
if (deliveryMethodInput=== 'Pick Up'){
const pickupLocationInput = document.getElementsByClassName('orderDetailSectionMidPickupLocationSelect')[0].value;
let pickupLocation = pickupLocationInput;
db.collection('OrderDatabase').doc(orderID).set({
PickupLocation:pickupLocation,
ShippingAddress:"",
BusinessName:businessName,
CustomerType:customerType,
BillingAddress:billingAddress,
CustomerPhoneNumber:customerPhoneNum,
DeliveryMethod:deliveryMethod,
ShipOrPickDate:shipOrPickDate,
PaymentMethod:paymentMethod,
Notes:notes,
OrderDate:orderDate,
OrderStatus:orderStatus,
OrderID:orderID,
salesRepID:salesRepID,
salesRepName:salesRepName,
})
db.collection('CustomerDatabase').get().then((snapshot) => {
snapshot.forEach((doc) => {
if(businessNameValue === doc.data().BusinessName){
let customerID = doc.data().CustomerID;
db.collection('OrderDatabase').doc(orderID).update({
CustomerID:customerID,
})
}
})
})
let subtotalArray=[]; /////////////this is the block im having issue with
let sum = 0
const basket = document.getElementsByClassName('orderProductSectionMidItemContainer')
Array.from(basket).forEach((basketItem) => {
const basketItemName = basketItem.getElementsByClassName('orderProductSectionMidDisplayItemNameInput')[0];
const basketItemPrice = basketItem.getElementsByClassName('orderProductSectionMidDisplayPriceInput')[0];
const basketItemQuantity = basketItem.getElementsByClassName('orderProductSectionMidDisplayQuantitiyInput')[0];
const basketItemDiscount = basketItem.getElementsByClassName('orderProductSectionMidDisplayDiscountInput')[0];
const basketItemSubtotal = basketItem.getElementsByClassName('orderProductSectionMidDisplaySubtotal')[0];
subtotalArray.push(basketItemSubtotal.value);
console.log(basketItemName.value)
console.log(basketItemName.value)
console.log(basketItemPrice.value)
console.log(basketItemQuantity.value)
db.collection('OrderDatabase').doc(orderID).collection('Basket').doc('testing').set({
ItemName:basketItemName.value,
ItemPrice:basketItemPrice.value,
ItemQuantity:basketItemQuantity.value,
ItemDiscount:basketItemDiscount.value,
ItemSubtotal:basketItemSubtotal.value ,
})
console.log(subtotalArray)
})
console.log(basket)
for(let i = 0; i < subtotalArray.length; i++){
sum += parseFloat(subtotalArray[i]);
}/////////////block ends here
}else if(deliveryMethodInput === 'Ship'){
db.collection('OrderDatabase').doc(orderID).set({
ShippingAddress:shippingAddress,
PickupLocation:"",
BusinessName:businessName,
CustomerType:customerType,
BillingAddress:billingAddress,
CustomerPhoneNumber:customerPhoneNum,
DeliveryMethod:deliveryMethod,
ShipOrPickDate:shipOrPickDate,
PaymentMethod:paymentMethod,
Notes:notes,
OrderDate:orderDate,
OrderStatus:orderStatus,
OrderID:orderID,
salesRepID:salesRepID,
salesRepName:salesRepName,
})
db.collection('CustomerDatabase').get().then((snapshot) => {
snapshot.forEach((doc) => {
if(businessNameValue === doc.data().BusinessName){
let customerID = doc.data().CustomerID;
db.collection('OrderDatabase').doc(orderID).update({
CustomerID:customerID,
})
}
})
})
}
})
})

How to get data with axios from all api pages?

What am I doing wrong? I want to get data from all pages in api. After adding the while it stopped working, but I don't know how to start the page loop differently!
getCustomers: function() {
let url = '/crm/customer/';
return axios.get(url).then((response) => {
this.customers = response.data.results;
if (this.customers.length === 100) {
let i = 2;
axios.get('/crm/customer/?page=' + i).then((response) => {
this.c = response.data.results;
i += 1;
for (let item of this.c.values()) {
this.customers.push(item);
}
while (this.c.length === 100) {
axios.get('/crm/customer/?page=' + i).then((response) => {
this.c = response.data.results;
i += 1;
for (let item of this.c.values()) {
this.customers.push(item);
}
}).catch( error => {}).finally(() => (global_waiting_stop()));
}
}).catch( error => {}).finally(() => (global_waiting_stop()));
}
}).catch( error => {}).finally(() => (global_waiting_stop()));
},
What I would do is, first, use an async function called getPageOfResults:
async function getPageOfResults(page) {
const response = await axios.get('/crm/customer/?page=' + page);
return response.data.results; // .results.values()? I don't know
}
Then, inside another async function, you can have a loop that does what you want:
async function getAllResults() {
const customers = [];
let lastResultsLength = 100;
let page = 1;
while (lastResultsLength === 100) {
const newResults = await getPageOfResults(page);
page++;
lastResultsLength = newResults.length;
customers = customers.concat(newResults);
}
return customers;
}
So you've got a variable keeping track of what page your on, and you keep on getting new pages until you get a page with less than 100 results.
You're adding all the results together with the concat function, and returning the entire list at the end.

Why is my function not returning the desired value, however, console.log() does the job?

I know this question might be a little basic but I think I am lacking some important fundamental concept. I am coding in node.js, and I have a function getPostInfo(). In the end, I have a return statement that returns the object that I created. However, when I run it on PowerShell I get no output. Furthermore, when I console.log(object) I get the required answer.
If someone knows what I might be doing wrong, let me know.
P.S. - The major chunks of code in the middle can be skipped as they are just to get information of a webpage
const cheerio = require('cheerio');
const axios = require('axios');
let object = {};
const getPostInfo = async () => {
const {data} = await axios.get('https://www.imdb.com/search/title/?groups=top_1000&ref_=adv_prv');
// console.log(data)
const $ = cheerio.load(data);
const titles = [];
const date = [];
const runtime = [];
const rating = [];
const metascore = [];
const votes = [];
const grossEarning = [];
$('h3 a').each((i, el) => {
titles[i] = $(el).text().trim();
})
$('h3 .lister-item-year').each((i, el) => {
date[i] = $(el).text();
})
$('.runtime').each((i, el) => {
runtime[i] = $(el).text().trim();
});
$('.ratings-imdb-rating').each((i, el) => {
rating[i] = $(el).text().trim();
})
$('.ratings-bar').each((i, el) => {
if ($(el).find('.ratings-metascore .favorable').length > 0) {
metascore[i] = $(el).find('.ratings-metascore .favorable').text().trim();
}
if ($(el).find('.ratings-metascore .mixed').length > 0) {
metascore[i] = $(el).find('.ratings-metascore .mixed').text().trim();
}
})
const nv = [];
$('.sort-num_votes-visible').each((i, el) => {
if($(el).find('span')){
// text-muted has text 'votes:', however we need the number of votes which is in next() span tag
nv[i] = $(el).find('.text-muted').next().text();
votes[i] = nv[i].split('$')[0];
grossEarning[i] = '$' + nv[i].split('$')[1];
}
})
for (let i = 0; i < 50; i++) {
object[i] = {
title: titles[i],
date: date[i],
runtime: runtime[i],
rating: rating[i],
metascore: metascore[i],
votes: votes[i],
grossEarning: grossEarning[i]
};
}
// This does not work but console.log(object) gives me a list of objects
return object
// console.log(object);
}
getPostInfo()

Passing Arguments to function in javascript

I have a function(case) as below,
let fn = (() => {
let ab = {};
let register = () => {
console.log("hello" + ab[x])
};
return (x,y) => {
ab[x] = y;
return register();
};
})();
this function is working only when I call as below,
let x = 'key';
let y = 'value';
fn(x,y);
Is there any chance to call directly like
fn('key', 'value');
what changes I have to make in function to call directly
The problem is your register function doesn't know about x. You need to pass it through from your previous function:
let fn = (() => {
let ab = {};
let register = (x) => {
console.log("hello" + ab[x])
};
return (x,y) => {
ab[x] = y;
return register(x);
};
})();
fn("key", "value");
I believe this is because you aren’t defining the parameters in the functions.
let fn = ((x,y) => {
let ab = {};
let register = () => {
console.log("hello"+ab[x])};
return (x,y) => {
ab[x]=y;
return register();
};
})();

How do I assign a variable defined in a synchronous scope in an async function?

I am trying to write a GUI frontend that uses a service to get data about the system. I am using a net.Socket for the client end of this. I want to be able to access certain variables assigned in the data event handlers in other modules but the assignment does not stay after that callback function finishes.
Problematic code:
client.on('data', (data) => {
var array = [...data];
array.splice(0,2);
for (var i=0;i<array.length;i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
module.exports.hasBattery = lastBatteryJSON.hasBattery == 'true';
module.exports.isCharging = lastBatteryJSON.isCharging == 'true';
module.exports.lastBatteryReading = parseFloat(lastBatteryJSON.batteryLife);
}
dataInBuffer = '';
});
Those three exported variable assignments don't actually work, the variables always either stay undefined or their default values outside of the function. I tried using a Promise to solve this problem but got the same result. I'm at a loss and I can't find any other questions or forum posts that solve this problem.
EDIT
I do not have the option of moving the code that depends on those variables into the callback. In order to do that I would have to wait for the data every frame and flood the server as a result.
As apple commented; you can export an object and mutate it every time you receive data:
const data = {};
client.on('data', (data) => {
var array = [...data];
array.splice(0, 2);
for (var i = 0; i < array.length; i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
//mutate the data object
data.hasBattery = lastBatteryJSON.hasBattery == 'true';
data.isCharging = lastBatteryJSON.isCharging == 'true';
data.lastBatteryReading = parseFloat(lastBatteryJSON.batteryLife);
}
dataInBuffer = '';
});
//export the data object
module.exports.batteryData = data;
Or as CertainPerformance answered you can have the caller decide when to ask for the information and provide a promise.
Here is an extended version of CertainPerformance answer that listens to error as well so a promise can be rejected and cleans up the event listeners when promise is resolved or rejected:
//wrapper for client.on to add and remove event listeners
const listeners = (function(){
var listenerCounter = -1;
const listeners = [];
const triggerEvent = event => data =>{
listeners.filter(
listener=>listener[2] === event
).forEach(
listener=>listener[1](data)
);
};
client.on('data', triggerEvent("data"));
client.on('error', triggerEvent("error"));//assuming you have an error event
return {
add:(event,fn)=>{
listenerCounter = listenerCounter + 1;
if(listenerCounter>1000000){
listenerCounter=0;
}
listeners.push([listenerCounter,fn,event]);
return listenerCounter;
},
remove:num=>{
listeners = listeners.filter(
listener=>{
num !== listener[0];
}
)
}
}
}());
//convert data to object or false
const getObjectFromData = data => {
var array = [...data];
var dataInBuffer="";
array.splice(0,2);
for (var i=0;i<array.length;i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
return {
hasBattery : lastBatteryJSON.hasBattery == 'true',
isCharging : lastBatteryJSON.isCharging == 'true',
lastBatteryReading : parseFloat(lastBatteryJSON.batteryLife)
};
}
return false;
}
//export this function
const getBatteryData = () =>
new Promise((resolve,reject) => {
const removeListeners = ()=>{
listeners.remove(okId);
listeners.remove(errorId);
}
const okId = listeners.add(
"data",
data=>{
const resultObject = getObjectFromData(data);
if(resultObject){
resolve(data);
removeListeners();//clean up listeners
}else{
//not sure of on data is triggered multiple times by client.on.data
// if it is then at what point do we need to reject the returned promise?
}
}
)
const errorId = listeners.add(
"error",
error=>{
reject(error);
removeListeners();//clean up listeners
}
)
});
//you can call getBatteryData like so:
//getBatteryData()
// .then(batteryData=>console.log(batteryData))
// .catch(error=>console.warn("an error getting battery data:",error))
Your module should export a function that returns a promise that returns the desired values. Also, use const and not var when possible:
let resolveObj;
const haveData = new Promise((resolve) => {
let resolved = false;
client.on('data', (data) => {
const array = [...data];
array.splice(0, 2);
for (let i = 0; i < array.length; i++) {
dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
}
console.log(dataInBuffer);
if (dataInBuffer.startsWith('batStat')) {
const {
hasBattery,
isCharging,
batteryLife,
} = JSON.parse(dataInBuffer.split(';')[1]);
resolveObj = {
hasBattery: hasBattery === 'true',
isCharging: isCharging === 'true',
lastBatteryReading: Number(batteryLife),
};
if (!resolved) resolve();
resolved = true;
}
dataInBuffer = '';
});
});
const getData = () => haveData.then(() => resolveObj);
module.exports = getData;
Then consume with
moduleFunction().then(({ hasBattery, isCharging, lastBatteryReading }) => {
// do something with results
});
If called before resolveObj is populated, the promise will wait until the first client.on('data' to resolve. After that, the function will return a promise that resolves immediately to the current value of resolveObj (which will be properly updated on client.on('data')

Categories

Resources