Object properties are undefined, the object itself shows all the data though - javascript

I'm building an Single Page application for a minor project. I've been trying to save data from API call's to the Movie Database in an object. If i console.log the object, I can see all its properties and values. If I console.log the object.property, it returns 'undefined'. This is the code:
(() => {
"use strict"
/* Saving sections to variables
--------------------------------------------------------------*/
const movieList = document.getElementsByClassName('movie_list')[0];
const movieSingle = document.getElementsByClassName('movie_single')[0];
/* All standard filters for displaying movies
--------------------------------------------------------------*/
const allFilters = {
trending: 'movie/popular',
toplist: 'movie/top_rated',
latest: 'movie/now_playing',
upcoming: 'movie/upcoming'
};
const allData = {};
/* Initialize app - Get al standard data and save it in object
--------------------------------------------------------------*/
const app = {
init() {
getData(allFilters.trending, 'popular');
getData(allFilters.toplist, 'toplist');
getData(allFilters.latest, 'latest');
getData(allFilters.upcoming, 'upcoming');
this.startPage();
},
startPage() {
window.location.hash = "trending";
}
}
/* Function for getting data from the API
--------------------------------------------------------------*/
const getData = (filter, key) => {
const request = new XMLHttpRequest();
const apiKey = '?api_key=xxx';
const getUrl = `https://api.themoviedb.org/3/${filter}${apiKey}`;
request.open('GET', getUrl, true);
request.onload = () => {
if (request.status >= 200 && request.status < 400) {
let data = JSON.parse(request.responseText);
data.filter = key;
cleanData.init(data);
} else {
window.location.hash = 'random';
}
};
request.onerror = () => {
console.error('Error');
};
request.send();
};
/* Check if the data is list or single, and clean up
--------------------------------------------------------------*/
const cleanData = {
init(originalData) {
if (!originalData.results) {
this.single(originalData);
} else {
allData[originalData.filter] = originalData;
}
},
list(data) {
data.results.map(function(el) {
el.backdrop_path = `https://image.tmdb.org/t/p/w500/${el.backdrop_path}`;
});
let attributes = {
movie_image: {
src: function() {
return this.backdrop_path;
},
alt: function() {
return this.title;
}
},
title_url: {
href: function() {
return `#movie/${this.id}/${this.title}`;
}
}
}
showList(data.results, attributes);
},
single(data) {
data.poster_path = `https://image.tmdb.org/t/p/w500/${data.poster_path}`;
data.budget = formatCurrency(data.budget);
data.revenue = formatCurrency(data.revenue);
data.runtime = `${(data.runtime / 60).toFixed(1)} uur`;
data.imdb_id = `http://www.imdb.com/title/${data.imdb_id}`;
let attributes = {
movie_image: {
src: function() {
return this.poster_path;
},
alt: function() {
return this.title;
}
},
imdb_url: {
href: function() {
return this.imdb_id
}
},
similar_url: {
href: function() {
return `#movie/${this.id}/${this.title}/similar`
}
}
};
showSingle(data, attributes);
}
};
const showList = (cleanedData, attributes) => {
movieList.classList.remove('hidden');
movieSingle.classList.add('hidden');
Transparency.render(movieList, cleanedData, attributes);
};
const showSingle = (cleanedData, attributes) => {
movieSingle.classList.remove('hidden');
movieList.classList.add('hidden');
Transparency.render(movieSingle, cleanedData, attributes);
}
const formatCurrency = amount => {
amount = amount.toFixed(0).replace(/./g, function(c, i, a) {
return i && c !== "." && ((a.length - i) % 3 === 0) ? '.' + c : c;
});
return `€${amount},-`;
};
app.init();
console.log(allData); // Returns object with 4 properties: trending, toplist, latest & upcoming. Each property is filled with 20 results (movies with data) from the API.
console.log(allData.trending) // Returns 'undefined' (each property I've tried).
console.log(allData['trending']) // Returns 'undefined'
Object.keys(allData); // Returns an empty Array []
})();
When I use console.log(allData) I see 4 properties, all filled with the results from the API. But when i do console.log(allData.trending) or console.log(allData['trending']) it returns 'Undefined' in the console. Has anyone an idea how to fix this?

When you call app.init() it fires the init and sends the api call(s) to fetch data.
The call to fetch data is Asynchronous, which means it doesn't wait for the response to continue execution. So it goes ahead and executes next lines of code, which are your console.logs. At this time the API calls haven't responded with the data, so when you try to access data.property it fails as the data is not yet here.
When you do log(data) it creates a log of the reference to data, which gets updated in the log when the reference is filled with a value later. To get the value of data at that instance and prevent updating later you can try log(JSON.stringify(data)). When you do that you get a consistent result, none of your logs work, which is the actual behaviour.
To get your logs work, look into the success/load callback of you A(synchronous)JAX request, log it from there. Or if you constructed allData later in cleanData then call the logs after cleanData function.
So to answer your question, none of your logs should work as it is an asynchronous call. You are getting the allData logged due to the way console.log works with references that are updated later, use console.log(JSON.stringify(allData)) to get the actual snapshot of Object

Related

Object element returning undefined with addEventListener

I am making a simple form which requests the user to input their project name and their name, along with some other info.
But each type of project has a different page, so to avoid copying and pasting the same getElementById and addEventListener functions in each page, I've made a module with those functions so every page handles it as needed. This is one of the pages:
// (imports)...
let project = {
author: null,
name: null,
mission: {
file: null,
path: null,
},
sd: {
code: null,
path: null,
},
modloaderfolder: null,
};
window.addEventListener("DOMContentLoaded", () => {
project.mission = handleMission();
project.sd = handleSD();
project.modloaderfolder = handleModloader();
project.name = handleName();
project.author = handleAuthor();
// ...
The problem is that the objects' elements are returning undefined in the two following functions:
export function handleName() {
const project = {};
const txt_name = document.getElementById("name");
txt_name.addEventListener("change", () => {
project.name = txt_name.value;
});
return project.name;
}
export function handleAuthor() {
const project = {};
const txt_author = document.getElementById("author");
txt_author.addEventListener("change", () => {
project.author = txt_author.value;
});
return project.author;
}
// Returns undefined
Whats intriguing for me is that some other functions are working as intended, I can't find out why. These are the corretly working functions:
export function handleSD() {
const project = { sd: {} };
const input_sd = document.getElementById("sd");
input_sd.addEventListener("change", () => {
document.getElementById("sel-sd").innerHTML = "";
Array.from(input_sd.files).forEach((file) => {
document
.getElementById("sel-sd")
.insertAdjacentHTML("beforeend", `<option>${file.name}</option>`);
});
if (!input_sd.files[0]) return;
const path = input_sd.files[0].path;
project.sd.path = path.substring(0, path.lastIndexOf("\\")) + "\\";
project.sd.code = project.sd.path.substring(
project.sd.path.lastIndexOf("\\") - 5,
project.sd.path.length - 1
);
});
return project.sd;
}
// This function correctly returns "sd: { path: ..., code: ...}"
What I noticed is that by returning an object, it returns and updates correctly for each change, but while returning an object's element, it aways returns undefined.
which is empty and nothing will change it because copying a primitive value is by value but the object that holds the key of the string, and as soon as the onchange function is activated it changes the value of your key in the object and it automatically changes in your object too because it is copied by Reference.
You can read more about this topic here

Assignment of Nuxt Axios response to variable changes response data content

async fetch() {
try {
console.log(await this.$api.events.all(-1, false)); // <-- First log statement
const res = await this.$api.events.all(-1, false); // <-- Assignment
console.log(res); // <-- Second log statement
if (!this.events) {
this.events = []
}
res.data.forEach((event, index) => {
const id = event.hashid;
const existingIndex = this.events.findIndex((other) => {
return other.hashid = id;
});
if (existingIndex == -1) {
this.events.push(events);
} else {
this.events[existingIndex] = event;
}
});
for (var i = this.events.length - 1; i >= 0; --i) {
const id = this.events[i].hashid
const wasRemoved =
res.data.findIndex((event) => {
return event.hashid == id
}) == -1
if (wasRemoved) {
this.events.splice(i, 1)
}
}
this.$store.commit('cache/updateEventData', {
updated_at: new Date(Date.now()),
data: this.events
});
} catch (err) {
console.log(err)
}
}
// The other functions, maybe this somehow helps
async function refreshTokenFirstThen(adminApi, func) {
await adminApi.refreshAsync();
return func();
}
all(count = -1, description = true) {
const func = () => {
return $axios.get(`${baseURL}/admin/event`, {
'params': {
'count': count,
'description': description ? 1 : 0
},
'headers': {
'Authorization': `Bearer ${store.state.admin.token}`
}
});
}
if (store.getters["admin/isTokenExpired"]) {
return refreshTokenFirstThen(adminApi, func);
}
return func();
},
Both log statements are giving slightly different results even though the same result is expected. But this only happens when is use the function in this specific component. When using the same function in other components, everything works as expected.
First data output:
[
{
"name": "First Name",
"hashid": "VQW9xg7j",
// some more correct attributes
},
{
"name": "Second name",
"hashid": "zlWvEgxQ",
// some more correct attributes
}
]
While the second console.log gives the following output:
[
{
"name": "First Name",
"hashid": "zlWvEgxQ",
// some more correct attributes, but this time with reactiveGetter and reactiveSetter
<get hashid()>: reactiveGetter()
​​ length: 0
​​​​ name: "reactiveGetter"
​​​​ prototype: Object { … }
​​​​<prototype>: function ()
​​​<set hashid()>: reactiveSetter(newVal)
​​​​length: 1
​​​​name: "reactiveSetter"
​​​​prototype: Object { … }
​​​​<prototype>: function ()
},
{
"name": "Second name",
"hashid": "zlWvEgxQ",
// some more correct attributes and still without reactiveGetter and reactiveSetter
}
]
As it can be seen, somehow the value of my hashid attribute changes, when assigning the response of the function call.
The next weird behavior happening here, is that the first object where the hashid field changes also gets reactiveGetter and reactiveSetter (but the second object in the array does not get these).
So it looks to me like something is happening with the assignment that I don't know about. Another guess would be that this has something to do with the Vuex store, because I do not change the Vuex tore in the other place where I use the same function.
It is verified that the backend always sends the correct data, as this is dummy data, consisting of an array with two objects with some attributes. So no other data except this two objects is expected.
Can someone explain to me why this behavior occurs?
There are few problems...
Do not use console.log with objects. Browsers tend to show "live view" of object - reference
this.events.findIndex((other) => { return other.hashid = id; }); is wrong, you are using assignment operator (=) instead of identity operator (===). That's why the hashid of the first element changes...

Function arguments, object with values equal to an empty object

I'm currently working on a MongoDB Javascript developer code.
There is something that I can't wrap my mind around:
This method has some funky business going on in the arguments here. What does {filters = null, page = 0, moviesPerPage = 20} = {} mean? What do you achieve by equating it to an empty object?
static async getMovies({
filters = null,
page = 0,
moviesPerPage = 20,
} = {}) {
/// more code
...}
Here is the entire function for more context should you need it:
static async getMovies({
filters = null,
page = 0,
moviesPerPage = 20,
} = {}) {
let queryParams = {}
if (filters) {
if ("text" in filters) {
queryParams = this.textSearchQuery(filters["text"])
} else if ("cast" in filters) {
queryParams = this.castSearchQuery(filters["cast"])
} else if ("genre" in filters) {
queryParams = this.genreSearchQuery(filters["genre"])
}
}
let { query = {}, project = {}, sort = DEFAULT_SORT } = queryParams
let cursor
try {
cursor = await movies
.find(query)
.project(project)
.sort(sort)
} catch (e) {
console.error(`Unable to issue find command, ${e}`)
return { moviesList: [], totalNumMovies: 0 }
}
/**
Ticket: Paging
Before this method returns back to the API, use the "moviesPerPage" and
"page" arguments to decide the movies to display.
Paging can be implemented by using the skip() and limit() cursor methods.
*/
// TODO Ticket: Paging
// Use the cursor to only return the movies that belong on the current page
const displayCursor = cursor.limit(moviesPerPage)
try {
const moviesList = await displayCursor.toArray()
const totalNumMovies = page === 0 ? await movies.countDocuments(query) : 0
return { moviesList, totalNumMovies }
} catch (e) {
console.error(
`Unable to convert cursor to array or problem counting documents, ${e}`,
)
return { moviesList: [], totalNumMovies: 0 }
}
}
This is a default parameter. It prevents the method from throwing an error if the parameter is not provided.
MDN reference
function getMovies({
filters = null,
page = 0,
moviesPerPage = 20,
} = {}) {
console.log('getMovies', filters, page, moviesPerPage)
}
function getMovies2({
filters = null,
page = 0,
moviesPerPage = 20,
}) {
console.log('getMovies2', filters, page, moviesPerPage)
}
getMovies({})
getMovies()
getMovies2({})
getMovies2()
It is default function parameter.
If when there's not any parameter, the {} would be default value of that prop.
This is necessary because if any parameter is not passed, the value becomes undefined.
In this case, an error occurs when if you attempting to get values such as filters and page from the undefined prop. so it seems that was used.

node.js edit variable from another file

Hello i have just started to learn node.js today and i am having a problem getting a variable from another file and editing it.
I'm trying to get the variable bots from server.js and edit it in bots.js.
server.js:
var bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(bots); }, 5000);
module.exports.bots = bots;
bots.js:
var request = require("../server.js");
var unique_id = '16f11103';
if (request.bots.some(e => e.botID === unique_id)) {
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
}
One way of doing this is to create a function that sets the variable bots.
Note: Instead of using seperate variables and functions i suggest to use something like a class to group all the functionality surrounding the bots.
server.js
var bots = [
{ botID: "16f11103", userID: "12345" },
{ botID: "5657d5e9", userID: "54321" },
];
var setBots = (newBots) => (bots = newBots);
setInterval(() => {
console.log(bots);
}, 5000);
module.exports = { bots, setBots };
bots.js
var { bots, setBots } = require("./server.js");
var unique_id = "16f11103";
if (bots.some((e) => e.botID === unique_id)) {
var newBots = bots.filter(function (e) {
return e.botID != unique_id;
});
setBots(newBots);
}
It's not working because of how variables are modified in JavaScript (Think copy by reference vs value).
When you first export the bots variable in server.js:
module.exports.bots = bots;
module.exports.bots is pointing to the bots variable by reference. If you modify the array directly like module.exports.bots[0] = 1 it will be reflected in the original bots variable as well since the reference hasn't changed.
However, what you're doing is reassigning the module.exports.bots value completely. The Array.prototype.filter() returns a new array without modifying the original.
When you do:
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
You're making it so that the module.exports.bots variable points to a new array and no longer points to the original bots array. That's why your console.log call is always printing the same array because the original bots variable is still pointing to that array and it was never modified.
If you don't want to modify the array directly you can try this.
server.js
var request = {};
request.bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(request.bots); }, 5000);
module.exports = request;
bots.js can now be kept exactly the same
var request = require("../server.js");
var unique_id = '16f11103';
if (request.bots.some(e => e.botID === unique_id)) {
request.bots = request.bots.filter(function(e) {
return e.botID != unique_id;
});
}
You should do
var bots = [
{ botID: '16f11103', userID: '12345' },
{ botID: '5657d5e9', userID: '54321' }
];
setInterval(() => { console.log(bots); }, 5000);
module.exports = bots;
instead of
module.exports.bots = bots;
You can also do
exports.bots = bots;
Then use Like this
var bots = require("./server.js");
var unique_id = '16f11103';
if (bots.some(e => e.botID === unique_id)) {
bots = bots.filter(function(e) {
return e.botID != unique_id;
});
}

Passing gathered value in API call from first method to second in same object

I have a object which contains two methods. The first one is calling API and storing response in variable.
In second method I execute first one using this.nameOfFirstMethod() and then I want to do some calculation basing on numbers which I collected in API call in first method.
To make it more clear take a look at code, start reading at second method:
this.currencyConverter = {
getRatio: function(selectedCurrency) {
var selectedCurrency = selectedCurrency;
$http({
url: 'http://api.fixer.io/latest?base='+selectedCurrency+'&symbols=PLN,CHF,EUR,USD,GBP',
method: 'GET'
})
.then(function(response) {
var currentCurrency = {
toPLN: response.data.rates.PLN,
toCHF: response.data.rates.CHF,
toEUR: response.data.rates.EUR,
toUSD: response.data.rates.USD,
toUSD: response.data.rates.GBP
};
console.log("Succesful store currentCurrency");
return currentCurrency;
}, function(response) {
console.log("Problem occure while downloading money current currency!");
console.log(response.data);
});
},
convertMoney: function(selectedCurrency,priceField) {
var priceField = priceField;
var selectedCurrency = selectedCurrency;
console.log('selectedCurrency in service: '+selectedCurrency);
console.log('priceField in service: '+priceField);
this.getRatio(selectedCurrency);
console.log(currentCurrency);
/*
var converted = {
PLN: function() { return priceField * $rootScope.currentCurrency.toPLN; },
USD: function() { return priceField * $rootScope.currentCurrency.toUSD; },
EUR: function() { return priceField * $rootScope.currentCurrency.toEUR; },
CHF: function() { return priceField * $rootScope.currentCurrency.toCHF; },
GBP: function() { return priceField * $rootScope.currentCurrency.toGBP; }
};
*/
}
}
Here is GIST of same code if someone doesn't like StackOverflow styling:
https://gist.github.com/anonymous/e03de4de1af407bf70f4038acd77c961
Please open this gist because I will now explain basing on specific line.
So in the line 30 I execute first method.
In line 9 I'm storing retrieved data in variable and in line 17 returning this data (in order to use it in second method).
Finally I want to console.log this in second object in line 32 (for now only console.log I will do my maths later on).
It doesn't work with this return, the line with console.log in second method cause following error:
ReferenceError: currentCurrency is not defined
you don't assign the return value of getRatio to a variable
it should be
currentCurrency = this.getRatio(selectedCurrency);
And you should work with promises correctly.
So change it to something like this (not tested)
this.currencyConverter = {
getRatio: function(selectedCurrency) {
var selectedCurrency = selectedCurrency;
return $http({
url: 'http://api.fixer.io/latest?base='+selectedCurrency+'&symbols=PLN,CHF,EUR,USD,GBP',
method: 'GET'
})
.then(function(response) {
var currentCurrency = {
toPLN: response.data.rates.PLN,
toCHF: response.data.rates.CHF,
toEUR: response.data.rates.EUR,
toUSD: response.data.rates.USD,
toUSD: response.data.rates.GBP
};
console.log("Succesful store currentCurrency");
return currentCurrency;
}, function(response) {
console.log("Problem occure while downloading money current currency!");
console.log(response.data);
});
},
convertMoney: function(selectedCurrency,priceField) {
var priceField = priceField;
var selectedCurrency = selectedCurrency;
console.log('selectedCurrency in service: '+selectedCurrency);
console.log('priceField in service: '+priceField);
var currentCurrency = this.getRatio(selectedCurrency);
currentCurrency.then(res => console.log(res));
//console.log(currentCurrency);
}
}

Categories

Resources