Assignment of Nuxt Axios response to variable changes response data content - javascript

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...

Related

JSON node undefined as variable

Using axios to return an object. I'm able to get the keys, etc. In this case, I'm trying to get the value for a child node "Id". When I pass in the Nodes as variables ([trait] [trait2]), I get "undefined". When I pass in [trait].Id it works. What am I doing wrong here? I tried different versions of Arr and Map, but I can't get those to work, but wonder if it's overkill anyway...
var trait = req.params.trait;
var trait2 = req.params.trait2;
axios(config)
.then(function (response) {
const resp_data = JSON.parse(xj.xml2json(response.data, { compact: true, spaces: 2 }));
if ( trait2="" ) {
resp2= resp_data.Application[trait];
} else {
resp2= resp_data.Application[trait];
console.log(resp2);
//CONSOLE RESPONSE:
// {
// Id: { _text: '34A9BCAA-4322-F5AA-9390-1639C06614D2' },
// BudgetedJointInd: { _text: 'Y' },.....
// }
trait2 = String(trait2); //="Id"
console.log(resp2[trait2]); //RETURNS UNDEFINED
//CONSOLE RESPONSE:
//undefined
console.log(resp2.Id); //WORKS CORRECTLY
//CONSOLE RESPONSE:
//{ _text: '34A9BCAA-4322-F5AA-9390-1639C06614D2' }
}
res.status(200).send(resp2.Id); //WORKS CORRECTLY
})
.catch(error => console.error(error))
}

How to create new object with limited data from other object in Vue JS

I am trying to create a search app that looks for products. I’ve got a PHP script that returns results in JSON format when a keyword is posted to it. I post to there with axios.
Now if there are 40 results for example the dropdown of results will be too long. So I want to create a new object from the object of 40 results and limit this object to 6.
I tried this by creating a function inside computed like this:
Object(this.responseData) is my full object with all results from my PHP script in a JSON object.
filteredData is my new object that I added in my data.
test(){
var fullResults = Object(this.responseData);
var number = 0;
for (var key in fullResults) {
number++;
if(!number > 6){
this.filteredData[number] += fullResults[key]
}
}
console.log(this.filteredData)
},
But my log just shows: Proxy {}. What am I doing wrong? I need to get all results from the old object, and add them to a new empty object with a limit of 6.
This is my full JS:
let app = Vue.createApp({
data: function(){
return{
// Object to fill with JSON response
responseData:{},
// Object to fill with filtered result (max 6)
filteredData: {},
showResultDiv: false,
totalResults: 0
}
},
methods:{
// Function to show loading dots until json returned
loadDiv(condition, content){
this.showResultDiv = condition
},
async fetchResults(){
await axios.post('includes/searchproducts_json.php', {
// Post data, add value of input to searchterm
searchterm: this.$refs.searchterm.value
})
.then((response)=>{
this.responseData = response.data
// Get length of returned object and add to totalResults
this.totalResults = Object.keys(this.responseData).length
console.log(Object(this.responseData));
})
.catch((error)=>{
console.log(error)
})
}
},
computed: {
filteredObject(){
return this.responseData
},
test(){
var fullResults = Object(this.responseData);
var number = 0;
for (var key in fullResults) {
number++;
if(!number > 6){
this.filteredData[number] += fullResults[key]
}
}
console.log(this.filteredData)
},
// Set totalResults with matching string
resultString(){
if(this.totalResults == 1){
return this.totalResults + ' resultaat'
}else{
return this.totalResults + ' resultaten'
}
}
}
})
app.mount('#v_search');
Your search result set should really be an array (of result objects).
Something like:
[
{"id": 1, "text": "foo"},
{"id": 2, "text": "bar"},
{"id": 3, "text": "baz"},
...
]
Or, more elegant:
{
"searchterm": "input value"
"total_results": 31,
"limit": 10,
"page": 2,
"results": [
{"id": 1, "text": "foo"},
{"id": 2, "text": "bar"},
{"id": 3, "text": "baz"},
...
]
}
Then, the computed should be as simple as:
data() {
return {
results: []
}
},
computed: {
resultsToShow() {
return this.results.slice(0, 6)
}
},
methods:{
fetchResults() {
axios.post('includes/searchproducts_json.php', {
// Post data, add value of input to searchterm
searchterm: this.$refs.searchterm.value
})
.then((response) => {
if (response.status === 200 && response.data.length) {
this.results = response.data
}
})
.catch(console.error)
}
}
See a working demo: https://codesandbox.io/s/thirsty-germain-i6w4w?file=/src/App.vue
And because it's just a simple slice, you could even remove (the overhead of) the computed property and just write it into the template inline expression:
<ol>
<li v-for="(result, index) in results.slice(0, 6)" :key="index">
{{ result.text }}
</li>
</ol>
Object.keys() returns an array of the object keys, which could be the thing you are looking for if i understand correctly. Then you could create a computed property something like this:
test() {
return Object.keys(this.responseData).map((key) => this.responseData[key]).slice(0,6);
}
This transforms the object to an an array after which you can use a simple slice method to get the first 6 elements.

Iterate deeply nested object with unknown level and add remove key/value based on user provided conditions

Could anyone please guide me how to achieve the below challenge which I am facing?
I have thousands of mock API request response JSON files. They are deeply nested, and they all are structured differently. I need to add/update/delete entry at the specfic location where the condition match which will be provided by user. I am not sure how to approach this problem? I have tried doing something like below. I am asking user for path for where to start looking. But this will increase time as user has to look for path in all file and pass that info to api. below code work upto 2 level only. need to search full tree where all user provides conditions matches, and at that place, I need to add/update/delete data. I took condition as an array of objects.
Draft Code
const _ = require("lodash");
const file = "./sample.json";
const actions = ["add", "delete", "update"];
const consumer = (file, key, where, data, action) => {
try {
const act = action.toLowerCase();
if(!actions.includes(act) throw new Error("invalid action provided");
if(_.isArray(where) && _.every(where, _.isObject())) throw new Error("no where clause condition provided");
let content = require(file);
let typeKeyContent = null;
let keyContent = _.get(content, key);
if(!keyContent) throw new Error("invalid key");
if(_.isArray(keyContent)) {
typeKeyContent = "array"
} else if (_.isObject(keyContent)) {
typeKeyContent = "object"
}
switch (act) {
case "add":
if (typeKeyContent === "array") {
// array logic
for (let i = 0; i < keyContent.length; i++) {
const result = where.every(element => {
for (let key in element) {
return keyContent[key] && element[key] === keyContent[key];
}
});
if (!result) {
console.log("attributes matching -> ", result);
return;
}
keyContent[i] = {...keyContent[i], ...data }
}
let newcontent = _.set(content, key, keyContent);
console.log("newcontent -> \n",JSON.stringify(newcontent, null, 2));
return;
}
const result = where.every(element => {
for (let key in element) {
return keyContent[key] && element[key] === keyContent[key];
}
});
if (!result) {
console.log("attributes matching -> ", result);
return;
}
keyContent = { ...keyContent, ...data };
let newcontent = _.set(content, key, keyContent);
console.log("newcontent -> \n",JSON.stringify(newcontent, null, 2));
// TODO :: store back in json file
break;
default:
console.log("reached default case");
return;
}
} catch(err) {
console.log("ERROR :: CONSUMER ::", error);
}
}
// AND based condition only
const conditions = [
{ name: "Essential Large" },
{ selected: true }
];
const newdata = { description: "our best service" } // wants to add new prop
consumer(file, "selected_items.essential", conditions, newdata, "add");
sample json
{
"status": 200,
"request": {},
"response": {
"ffs": false,
"customer": {
"customer_id": 1544248,
"z_cx_id": 123456
},
"selected_items": {
"essential": [
{
"id": 4122652,
"name": "Essential Large",
"selected": true,
"description": "our best service" // will be added
},
{
"id": 4122653,
"name": "Essential Large",
"selected": true,
"description": "our best service" // will be added
}
]
},
"service_partner": {
"id": 3486,
"name": "Some String",
"street": "1234 King St."
},
"subject": "Project",
"description": "Issue: (copy/paste service request details here Required"
}
}
So you want to go through every key of a nested object right?
function forEvery(object,fn){
//obj is the object, fn is the function
//this function should go through each item in an object loaded from JSON string
//fn takes in 3 arguments: current element, that element's parent, level of depth(starts at 1)
var arr=[]
function recurse(obj,map,depth){
Object.keys(obj).forEach((a,i)=>{
fn(obj[a],obj,a,depth) //because fn can affect the object so the if statement should after not before ;-;
if(typeof obj[a]=="object"&&obj[a]!=null){ //if nested value is another object
map.push(a); arr.push(map)
recurse(obj[a],[...map],depth+1)
}
})
}
recurse(object,[],1)
}
//usage would be like:
//let customerCondition=/*some logic here*/
//let testObj=JSON.parse( (require('fs')).readFileSync('dirToSomeFile.json') )
forEvery(testObj,customerCondition)
Here's a live example
let testObj={"status":200,"request":{},"response":{"ffs":false,"customer":{"customer_id":1544248,"z_cx_id":123456},"selected_items":{"essential":[{"id":4122652,"name":"Essential Large","selected":true},{"id":4122653,"name":"Essential Medium","selected":false}]},"service_partner":{"id":3486,"name":"Some String","street":"1234 King St."},"subject":"Project","description":"Issue: (copy/paste service request details here Required"}}
function forEvery(object,fn){
//obj is the object, fn is the function
//this function should go through each item in an object loaded from JSON string
//fn takes in 3 arguments: current element, that element's parent, level of depth(starts at 1)
var arr=[]
function recurse(obj,map,depth){
Object.keys(obj).forEach((a,i)=>{
fn(obj[a],obj,a,depth) //because fn can affect the object so the if statement should after not before ;-;
if(typeof obj[a]=="object"&&obj[a]!=null){ //if nested value is another object
map.push(a); arr.push(map)
recurse(obj[a],[...map],depth+1)
}
})
}
recurse(object,[],1)
}
//example usage
let userQuery=[{ name: "Essential Large" },{ selected: true }]; //the user query in the format you gave
let userCondition={} //assuming each key across userQuery is unique, I set a model object for comparisons later on
userQuery.forEach(obj=>{ //I fill the model object :D
Object.keys(obj).forEach(key=>{
userCondition[key]=obj[key]
})
})
let testFn=(elem,parent,key,depth)=>{
//I use comparisons with the model object
let condition=typeof elem!="object"?false:
Object.keys(userCondition)
.every(item=>userCondition[item]==elem[item])
//true if matches user condition(meaning elem must be an object), false otherwise
if(condition){
console.log(parent[key],"will now be deleted")
delete(parent[key]) //deletion example(if user conditions match)
}
}
forEvery(testObj,testFn)
console.log("and the changed object looks like",testObj)

Converting Async Response to Standard Javascript Object

Imagine your React app gets a response like this:
email_address
first_name
last_name
What's a best practice way to convert these fields to something more common in Javascript:
emailAddress
firstName
lastName
Also keeping mind that there could be nested structures.
I've typically done this immediately when the response is received.
My colleagues seem to think it's fine to keep the snake_case syntax persist through the app.
There may be some edge cases that fail, I could not find anything on github that would do the trick but if you have any errors then please let me know.
It is assuming you only pass object literals to it, maybe you can write some tests and tell me if anything fails:
const snakeToCamel = snakeCased => {
// Use a regular expression to find the underscores + the next letter
return snakeCased.replace(/(_\w)/g, function(match) {
// Convert to upper case and ignore the first char (=the underscore)
return match.toUpperCase().substr(1);
});
};
const toCamel = object => {
if (Array.isArray(object)) {
return object.map(toCamel);
}
if (typeof object === 'object' && object !== null) {
return Object.entries(object).reduce(
(result, [key, value]) => {
result[snakeToCamel(key)] = toCamel(value);
return result;
},
{}
);
}
return object;
};
console.log(
toCamel({
arra_of_things: [
{ thing_one: null },
{ two: { sub_item: 22 } },
],
sub_one: {
sub_two: {
sub_three: {
sub_four: {
sub_four_value: 22,
},
},
},
},
})
);

test case failing due to .map is not a function error

Hi i have a react component expenses-total.js and a corresponding test case expenses-total.test.js as shown below.
expenses-total.js
export default (expenses=[]) => {
if (expenses.length === 0) {
return 0;
} else {
return expenses
.map(expense => expense.amount)
.reduce((sum, val) => sum + val, 0);
}
};
expenses-total.test.js
import selectExpensesTotal from '../../selectors/expenses-total';
const expenses = [
{
id: "1",
description: "gum",
amount: 321,
createdAt: 1000,
note: ""
},
{
id: "2",
description: "rent",
amount: 3212,
createdAt: 4000,
note: ""
},
{
id: "3",
description: "Coffee",
amount: 3214,
createdAt: 5000,
note: ""
}
];
test('Should return 0 if no expenses', ()=>{
const res = selectExpensesTotal([]);
expect(res).toBe(0);
});
test('Should correctly add up a single expense', ()=>{
const res = selectExpensesTotal(expenses[0]);
expect(res).toBe(321);
});
test('Should correctly add up multiple expenses',()=>{
const res = selectExpensesTotal(expenses);
expect(res).toBe(6747);
});
when i run the test case, its getting failed by giving an error
TypeError: expenses.map is not a function
I know the test case is correct but dont know what is wrong with thecomponent.
Could anyone please help me in fixing this error?
The problem is with if (expenses.length === 0) and the test case that uses selectExpensesTotal(expenses[0]):
expenses[0] passes an object, which has no length property, so in the function being tested, expenses.length returns undefined. However, undefined === 0 evaluates to false so your code goes into the else block tries to use .map on the object, which doesn't have that function, thus it throws an error.
In a brief: you can't map over an object.
expenses is an array of objects, so expenses[0] is an object.
Condition expenses.length === 0 evaluates to false, since obviously .length property does not exist on Object.prototype, so the else condition takes place - your function tries to map over an object.
The problem is that expenses[0] is an object (you probably expected it to be an array) and an object does not have a map function. A quick hack would be to add another ifs into the loop to check if expenses is actually an object. So that:
export default (expenses=[]) => {
if (expenses.length === 0) {
return 0;
} else {
if (typeof expenses === 'object') {
return expenses.amount
} else {
return expenses
.map(expense => expense.amount)
.reduce((sum, val) => sum + val, 0);
}
}
};
I hope this help.
To fix this error, you can pass in an array of object into
selectExpensesTotal([expenses[0]])
rather than just an object
selectExpensesTotal(expenses[0])
So your code show look like this:
test('Should correctly add up a single expense', ()=>{
const res = selectExpensesTotal([expenses[0]]);
expect(res).toBe(321);
});
.map function will now work on expenses. Because, this is now an array of object ( works with map function ) and not an object(This does not work with map function)

Categories

Resources