Access keys within Javascript object - javascript

I have an iframe that I am sending a data object to that has an id_token and access_token in it. The data object arrives fine and I am able to pull the object with an event listener. Sample code is below. However, when I receive the object I try to access items in the object and get an undefined. I have included an obfiscated image of the object that I receive in the javascript console.
I have included snippets of different things I have tried but to no avail.
Displaying the iframe
const data = {
id_token: idToken,
access_token: accessToken
};
Application that is being displayed in the iframe
useEffect(() => {
window.addEventListener('message', receiveMessage, false);
function receiveMessage(event) {
if (event.origin !== 'https://example.com") {
return;
}
let eventData = event.data;
console.log(eventData); // The response is the object that is returned in the image below
console.log(eventData.id_token) // The response is undefined
console.log(eventData[id_token]) // undefined
console.log(eventData["id_token"] // undefined
console.log(eventData['"id_token"'] // undefined
// All three of these displayed each individual character of the object.
Object.keys(eventData).forEach(key => console.log(key, eventData[key]));
Object.getOwnPropertyNames(eventData).forEach(key =>
console.log(key, eventData[key])
);
for (let key in eventData) console.log(key, eventData[key]);
}
}, []);
Basically I need to extract the access and id tokens for authentication. If there is a better way to do this I am all ears.
Screenshot of object that is being returned:

When receiving "objects" from requests or events in Javascript it can be easy to mistake actual objects from a string-serialized form of an object in JSON notation.
It can be difficult to tell which is which when logging to the console as the example shows but browser-tools can be more helpful as the image shows.
If you need to convert your JSON back to an object then use JSON.parse() and handle any exceptions.
obj = {a:100}
json = JSON.stringify(obj);
// print an object
console.log('Printing an Object:')
console.log(obj)
// print json
console.log('Printing a JSON string:')
console.log(json)

Related

I can't access the elements of an array via bracket notation in the frontend but it works in the backend

I am new to coding and I am experiencing a really strange problem. There is an array coming from a database in the backend and I want to access the single elements of the array in the frontend via bracket notation. I can access the single elements of the array in the backend. But in the frontend, I receive an error message when I try to access the elements.
Here is my GitHub repo for the project: https://github.com/LittleWing85/Homepage_For_A_Friend
And you can see the website here: https://website-for-portfolio.herokuapp.com/
In /client/src/modules/Project.js, I am retreiving an object with data from the backend and storing the object in a variable called project:
export default function Project() {
const dispatch = useDispatch();
const projectId = useSelector((state) => state.portfolio.projectId);
const [project, setProject] = useState({});
useEffect(() => {
fetch("/api/project/" + projectId)
.then((response) => response.json())
.then((data) => {
setProject(data);
});
}, [projectId]);
The object has a property with the key gallery_pictures and its value is an array. With this code I was trying to display the first element of that array:
return (
<div className="content">
...
<p>{project.gallery_pictures[0]}</p>
In the console of my browser I receive the error message project.gallery_pictures is undefined:
screenshot of error message
But if I change the code <p>{project.gallery_pictures[0]}</p> to <p>{project.gallery_pictures}</p>, the content of the array is displayed:
screenshot of displayed content from array
For this reason I don't understand why the console says that project.gallery_pictures is undefined.
I tried to access the elements of this array in the backend console in /server/server.js with the same approach in this code and it worked fine:
const express = require("express");
...
app.get("/api/project/:id", (request, response) => {
getProjectDataById(request.params.id).then((result) => {
response.json(result);
console.log(Array.isArray(result.gallery_pictures));
console.log(result.gallery_pictures[0]);
> });
> });
screenshot of working code in backend
What is it that I am not getting? Why can't I access the elements of the array in the frontend?
Within the useEffect you fetch data from your sever and, as soon as you receive a response, set project to the received data. However, while waiting for the server response project is still the initial value {}, thus project.gallery_pictures is still undefined.
As a solution, you could add a condition that checks if project is still undefined, and if yes instead render a loading screen.
This is happening because the request hasn't completed by the time that your component attempts to render itself. Inside your component adding a condition will do the trick
{project?.gallery_pictures !== undefined && Array.isArray(project.gallery_pictures) ? project.gallery_pictures[0] : null}

Why is the collection not passed to the client side

I create a map object and write down the id at the key, the username as the value, everything works fine on the server side, the collection is output to the console, but when I pass it to the client side, it is either not transferred correctly or does not work at all. Everything worked fine with the array.
Server:
socket.on('connect user', (userName) => {
//users.push(userName.userConnect);
users.set(id++, userName.userConnect); // map users
console.log(users);
io.emit("users", users); // passing the collection
Client:
const addUserToModalWindow = () => {
const contentWindow = document.querySelector(".usersList");
socket.on('users', (users) => {
for (let user of users) {
console.log(user);
}
});
};
Writes an error: users is not iterable.
The problem is because of the library you're using, socket.io converts the payload object to JSON during the communication between your client-side and server-side.
However, you're not able to convert a Map or Set object to JSON directly. You can have a try:
A screenshot of the document:
You're trying pass JavaScript objects through TCP socket (http, web-socket). TCP sockets are not language specific and cannot deal with native js objects. But they can work with strings and binary data.
But socket.io library is pretty smart and can figure out you're passing an object. It will run JSON.stringify on your value (will convert to string) then send it through the network and on the client side will convert it back to the object with JSON.parse.
Everything would work if simple objects would be used. But in this case ES6 Map is used. When JSON.stringify is used on map it will always be converted to empty object. Like this:
Server
const map = new Map();
map.set(1, { name: "Joh Doe" });
JSON.stringify(map); // '{}'
Client
JSON.parse(map); // {}
Then on client side you try to for of loop on empty object. This kind of loop will work on ES6 Map but not on object.
for (const v of {}) console.log(v) // is not iterable Error
for (const v of new Map()) console.log(v) // works fine
The solution to this problem is pretty simple: Don't try send ES6 Map. Use plain object instead.
Instead of this:
const users = new Map();
users.set(1, { name: "Joh Doe" });
Use this:
const users = {};
users[1] = { name: "Joh Doe" };
Client side:
socket.on('users', (users) => {
for (let user of Object.values(users)) {
console.log(user);
}
});

Receiving error when trying to use JSON.parse from internal storage data

My classmates and I are receiving the following error message when trying to use JSON.parse with data from internal storage. The code is given to us by our instructor so not sure what the issue is.
internal storage should hold nothing and initialize "pageViews" with 0 or if there is data in internal storage it should be an array.
Uncaught SyntaxError: Unexpected token o in JSON at position 1 at
JSON.parse () at HTMLDocument.addPageView
HERE IS OUR CODE.
const pageViewsKeyName = "pageViews";
function addPageView() {
let pageViews = localStorage.getItem(pageViewsKeyName);
let arr = [];
if (pageViews && pageViews.length > 0) {
// get the array stored in local storage at pageViewsKeyName
arr = JSON.parse(pageViews);
// now we're able to insert an item in the page view data
let newPageData = {
"path": window.location.pathname,
"timestamp": moment()
};
// now add new page data to the array
arr.push(newPageData);
// finally, we want to update our storage with the most up to date array
localStorage.setItem(pageViewsKeyName, arr);
}
}
JSON.parse is used to parse the string JSON value as JSON object. Looks like the pageViews already contains JSON object. Therefore you don't need to parse it. You can console.log(pageViews) to confirm that.
Yes, console.log(typeof PageViews) and check out what is it's data type.
If it's an arry:
arr.push(PageViews[0]); // TODO for-loop to push all views into arr
If it's an object:
arr.push(PageViews)

dataTransfer.getData is empty even after dataTransfer.setData is called, does it have to do with fetch()?

I need to get data from essentially an online excel spreadsheet. Previously, I was retrieving this data from the actual HTML DOM elements and their children, using .innerText or .value. I realized that this would be inconsistent, since the excel spreadsheet can have more or less rows depending on what the user wants. I changed up the way I get data by using querying the database that the spreadsheet is stored in. This way, it wouldn't matter how many columns this spreadsheet has, I can retrieve only the data I want from the columns in the database.
I made sure that the data I am retrieving from the database is valid, the data presented to dataTransfer.setData() is the exact same as before when I was retrieving information from DOM elements, and the object I am trying to transfer is indeed a string. When I step through from onDragStart() to onDrop(), it seems like either dataTransfer.setData isn't actually setting the data, or dataTransfer.getData isn't actually getting the data, which seems very unlikely. The only thing I can think of is that I introduced a fetch statement, and am trying to set the data in the promise of the fetch, which might make things weird for some reason.
BEFORE:
function onDragStart(e) {
let data = {
"item": e.target.children[2].children[0].value,
"description": e.target.children[3].textContent,
//etc...
};
let stringData = JSON.stringify(data);
e.dataTransfer.setData('text', stringData);
}
function onDrop(e) {
e.preventDefault();
let stringData = e.dataTransfer.getData('text');
let data = JSON.parse(stringData);
//do things with data...
}
AFTER:
function onDragStart(e) {
fetch('<my_url>', {method: 'POST', body: JSON.stringify(data)})
.then(function(response) {
return response.text();
})
.then(function(result) {
let parsedResult = JSON.parse(result);
let transferData = {
'item': parsedResult.item,
'description': parsedResult.description,
//etc...
};
let stringData = JSON.stringify(transferData);
e.dataTransfer.setData('text', stringData);
});
}
//onDrop() did not change
The transferData object in the second onDragStart() is the exact same as the first data object in the first. When I step through the onDrop(), I noticed that stringData is empty, so an error is thrown when trying to JSON.parse(), "unexpected end of JSON input."

$promise stays promised on 'then' function

I'm using angular's $resource to perform a GET operation. This is how I do it:
var query = {customerid: $stateParams.customerid}
$resource('api/reports/running_count').get(query).$promise.then(function(value) {
$scope.runningInstance = value;
});
I also tried it like that:
$resource('api/reports/running_count').get(query, function(value) {
$scope.runningInstance = value;
});
The request returns a number. I checked the response with chrome's developer-tools. The request is indeed sent as follows:
<base-url>/api/reports/running_count?customerid=<id>
The response returns a number, again as expected.
But when I put a breakpoint in the callback function, the value is again a promise and not a number. Not sure what I'm doing wrong.
The $resource service doesn't work when the response is a primative. It uses angular.copy to copy the data to the $resource object and angular.copy doesn't copy a primative to an object. It can only make deep copies of properties of other objects.
From the Docs:1
angular.copy
Creates a deep copy of source, which should be an object or an array.
If a destination is provided, all of its elements (for arrays) or properties (for objects) are deleted and then all elements/properties from the source are copied to it.
If source is not an object or array (inc. null and undefined), source is returned.
In your case the source was not an object and you were getting just the $promise property which the $resource service attaches to the resource object.
From angular example https://docs.angularjs.org/api/ngResource/service/$resource:
var User = $resource('/user/:userId', {userId:'#id'});
User.get({userId:123}, function(user, getResponseHeaders){
user.abc = true;
user.$save(function(user, putResponseHeaders) {
//user => saved user object
//putResponseHeaders => $http header getter
});
});
Just put you values there.
As far I know if you want to get data using query value then server get query value as a string. it is useful when you will send query from search box . for example want to search product by name or something else then you can use like.
in service:
var query = {productname: '123'}
getProducts: function(query) {
var getProducts = $resource('/api/products', {}, {
'get': {method: 'GET', isArray: true}
});
return getProducts.get(query);
}
and server side will receive as req.query.productname so productname as a string so you need to convert it to number if needed as number.
but if you want to find produt by id you should send id as a parameter not query for exactly matching that also may send as string.
for example in service:
getProductById: function(id) {
var getProductById = $resource('/api/products/:id', {id: '#id'}, {
'get': {method: 'GET'}
});
return getProductById.get({id: id});
}
so server side will receive as req.params.id

Categories