Simple question I am trying the following in my console
let a = new Proxy(new Date(), {})
I am expecting to be able to call
a.getMonth();
but it does not work it throws:
Uncaught TypeError: this is not a Date object.
at Proxy.getMonth (<anonymous>)
at <anonymous>:1:3
Funny part is that in Chrome the autocomplete does suggest all the Date functions on a. What am I missing?
Edit in response for #Bergi
I realized that there is a bug in this code aside for my question but here is what I am trying to do:
class myService {
...
makeProxy(data) {
let that = this;
return new Proxy (data, {
cache: {},
original: {},
get: function(target, name) {
let res = Reflect.get(target, name);
if (!this.original[name]) {
this.original[name] = res;
}
if (res instanceof Object && !(res instanceof Function) && target.hasOwnProperty(name)) {
res = this.cache[name] || (this.cache[name] = that.makeProxy(res));
}
return res;
},
set: function(target, name, value) {
var res = Reflect.set(target, name, value);
that.isDirty = false;
for (var item of Object.keys(this.original))
if (this.original[item] !== target[item]) {
that.isDirty = true;
break;
}
return res;
}
});
}
getData() {
let request = {
...
}
return this._$http(request).then(res => makeProxy(res.data);
}
Now getData() returns some dates
My original answer was all wrong. But the following handler should work
const handler = {
get: function(target, name) {
return name in target ?
target[name].bind(target) : undefined
}
};
const p = new Proxy(new Date(), handler);
console.log(p.getMonth());
Related
i have an interesting example of code that's not working like i'm expected.
I really dont understand why my obj wouldnt proxy.
I'm expect that obj ill proxy via link, but it's not. Can anyone explain how it's works and what I don't understand? Thank you!
let obj = {
foo: "123"
};
function test(fn, object) {
object = new Proxy(object, {
get(target, key) {
console.log('get');
return target[key];
},
set(target, key, value) {
console.log('set');
target[key] = value;
return true;
}
});
fn();
}
test(() => {
obj.foo = "helloworld";
console.log(obj.foo);
}, obj);
UPD: i want to assign object to proxy variable ONLY through arguments can i?
Either assign the proxy to obj
let obj = {
foo: "123"
};
function test(fn, object) {
obj = new Proxy(object, {
get(target, key) {
console.log('get');
return target[key];
},
set(target, key, value) {
console.log('set');
target[key] = value;
return true;
}
});
fn();
}
test(() => {
obj.foo = "helloworld";
console.log(obj.foo);
}, obj);
Or pass the proxy as an argument
let obj = {
foo: "123"
};
function test(fn, object) {
const o = new Proxy(object, {
get(target, key) {
console.log('get');
return target[key];
},
set(target, key, value) {
console.log('set');
target[key] = value;
return true;
}
});
fn(o);
}
test((o) => {
o.foo = "helloworld";
console.log(o.foo);
}, obj);
I'm trying to create a recursive method to find.
But I don't understand why it finds the element but returns undefined.
How can solve it, please?
Here is my code:
export const findDmaFromHierarchy = (hierarchy: [], value: string): any => {
let founded = undefined;
hierarchy.forEach((dma: any) => {
if (dma.children) {
findDmaFromHierarchy(dma.children, value);
}
if (String(dma.value) === String(value)) {
console.log("founded: ", dma);
founded = Object.assign({}, dma);
return founded;
}
});
return founded;
};
You don't set founded when the recursive call finds the value.
export const findDmaFromHierarchy = (hierarchy: [], value: string): any => {
let founded = undefined;
hierarchy.forEach((dma: any) => {
if (dma.children) {
founded = findDmaFromHierarchy(dma.children, value);
if (founded) {
return founded;
}
}
if (String(dma.value) === String(value)) {
console.log("founded: ", dma);
founded = Object.assign({}, dma);
return founded;
}
});
return founded;
};
You could take Array#some and return early on found.
Then I suggest to check if the value is found and assign the object and return with true. To move this check in front of the function omits unnecessary checks for children objects.
For children take a temporary variable and check it and assign and return if truthy.
export const findDmaFromHierarchy = (hierarchy: [], value: string): any => {
let found = undefined;
hierarchy.some((dma: any) => {
if (String(dma.value) === String(value)) {
console.log("founded: ", dma);
found = Object.assign({}, dma);
return true;
}
if (dma.children) {
let temp = findDmaFromHierarchy(dma.children, value);
if (temp) {
found = temp;
return tru;
}
}
});
return found;
};
I stack here today.
I make an axios call to my api.
On succes response I run the function and make a chart on my page from recived data.
It work fine while I leave all aditional transformation inside the method
mounted() {
this.month = moment().month("June").format("YYYY-MM");
axios.get('/data/statistic2/'+this.month)
.then(response => {
this.set = response.data;
this.generateChart(response.data);
})
},
methods: {
generateChart(input) {
let data = [];
input.forEach(function(row) {
let item = {};
item.day = row.day;
let timeArray = [row.time1, row.time2,row.time3,row.time4,row.time5];
let result = timeArray.filter(function(item) {
return item !== null;
}).reduce((prev, current) => parseInt(prev) + parseInt(current));
item.time = result;
data.push(item);
})
this.datachart = data;
},
But when I try to incapsulate this bit of logic in separate method
mounted() {
this.month = moment().month("June").format("YYYY-MM");
axios.get('/data/statistic2/'+this.month)
.then(response => {
this.set = response.data;
this.generateChart(response.data);
})
},
methods: {
generateChart(input) {
let data = [];
input.forEach(function(row) {
let item = {};
item.day = row.day;
item.time = convertTimeFromDB(row);
data.push(item);
})
this.datachart = data;
},
convertTimeFromDB(row) {
let timeArray = [row.time1, row.time2,row.time3,row.time4,row.time5];
return timeArray.filter(function(item) {
return item !== null;
}).reduce((prev, current) => parseInt(prev) + parseInt(current));
},
I got "Uncaught (in promise) ReferenceError: convertTimeFromDB is not defined"
You should change convertTimeFromDB(row) to this.convertTimeFromDB(row), and change the function(row) {} to an arrow function (row => {}):
generateChart(input) {
let data = [];
input.forEach((row) => {
let item = {};
item.day = row.day;
item.time = this.convertTimeFromDB(row);
data.push(item);
})
this.datachart = data;
},
This has nothing to do with promises.
convertTimeFromDB is a property of the methods object. It isn't a variable (in scope or otherwise).
You have to refer to it in the context of the object (e.g. whatever.methods.convertTimeFromDB)
I have this object
let x = {
"name": "Ola",
"dates": [
{
"7.01.2020": [1, 2, 3]
},
{
"8.01.2020": [3 ,4]
}
],
"id": 7
}
and I need to be able to delete on button click chosen element from array. Ex. after button click, my object looks like this:
let x = {
"name": "Ola",
"dates": [
{
"7.01.2020": [1, 2]
},
{
"8.01.2020": [3 ,4]
}
],
"id": 7
}
Acrtually I've managed to filter this arr, the problem is when I try to return new x.dates arr. Please, notice that there are objects with different keys.
Any ideas? Thanks!
EDIT - whole func
try {
fetch(`http://localhost:3003/users/${userObject.id}`)
.then(res => res.json())
.then(user => {
const map = new Map(
user.dates.map((entry, i, arr) => {
const [key] = Object.keys(entry);
console.log(entry);
return [key, entry[key]];
})
);
result = map.get(date.toLocaleDateString());
console.log(user.dates);
return user;
})
.then(user => {
// Create array without deleted task
userDatesArr = result.filter(el => {
if (el !== itemText) {
return el;
}
});
result = userDatesArr;
// Update json-server - doesn't work, cause user.dates looks the same
patchFunc(user.dates);
});
} catch (err) {
console.error(err);
}
;
There are a few issues with that code:
You're assigning to a variable that isn't declared anywhere (result). That means the code is falling prey to what I call The Horror of Implicit Globals. You need to declare your variables. I strongly recommend turning on strict mode so the JavaScript engine will tell you when you do this.
That's now how you use filter, the callback should return a flag saying whether to keep the entry.
You're not checking for HTTP success. This is a footgun in the fetch API I write about here. You need to check ok on the response before calling json.
itemText is a string, but your array contains numbers. No string is ever === a number.
There is no reason for separating the code in your first then handler from the second, there's no intervening promise.
There's no reason to create the Map if you're not going to reuse it. You can just find the object once.
Nothing in the code handles errors the ajax call or the fulfillment handlers. (The try/catch you have wrapped around it will only catch errors calling fetch, not errors in the fetch operation of subsequent then handlers.)
Here's an updated version with some notes:
fetch(`http://localhost:3003/users/${userObject.id}`)
.then(res => {
// *** Check for HTTP success
if (!res.ok) {
throw new Error("HTTP error " + res.status);
}
return res.json();
})
.then(user => {
const targetValue = +itemText; // *** Convert to number
for (const entry of user.dates) {
const targetKey = Object.keys(entry)[0];
if (targetKey === key) {
// *** Remove the entry if present
entry[targetKey] = entry[targetKey].filter(value => value !== targetValue);
break;
}
}
// Update json-server
patchFunc(user.dates);
});
Note that that doesn't flag it up if the object for the target date isn't found. If you want to do that, you can add a flag:
fetch(`http://localhost:3003/users/${userObject.id}`)
.then(res => {
// *** Check for HTTP success
if (!res.ok) {
throw new Error("HTTP error " + res.status);
}
return res.json();
})
.then(user => {
const targetValue = +itemText; // *** Convert to number
let found = false;
for (const entry of user.dates) {
const targetKey = Object.keys(entry)[0];
if (targetKey === key) {
// *** Remove the entry if present
entry[targetKey] = entry[targetKey].filter(value => value !== targetValue);
found = true;
break;
}
}
if (!found) {
// *** Handle the fact it wasn't found
}
// Update json-server
patchFunc(user.dates);
});
You'll also want to add a rejection handler (.catch) to handle errors.
I think the below code snippet will give a better understanding of the problem.
Check the console, you will get the desired output. You need to do a deep cloning to avoid mutating the existing object (Deep Cloning - thanks to #nemisj)
let x = {
"name": "Ola",
"dates": [{
"7.01.2020": [1, 2, 3]
},
{
"8.01.2020": [3, 4]
}
],
"id": 7
}
function clone(item) {
if (!item) { return item; } // null, undefined values check
var types = [ Number, String, Boolean ],
result;
// normalizing primitives if someone did new String('aaa'), or new Number('444');
types.forEach(function(type) {
if (item instanceof type) {
result = type( item );
}
});
if (typeof result == "undefined") {
if (Object.prototype.toString.call( item ) === "[object Array]") {
result = [];
item.forEach(function(child, index, array) {
result[index] = clone( child );
});
} else if (typeof item == "object") {
// testing that this is DOM
if (item.nodeType && typeof item.cloneNode == "function") {
result = item.cloneNode( true );
} else if (!item.prototype) { // check that this is a literal
if (item instanceof Date) {
result = new Date(item);
} else {
// it is an object literal
result = {};
for (var i in item) {
result[i] = clone( item[i] );
}
}
} else {
// depending what you would like here,
// just keep the reference, or create new object
if (false && item.constructor) {
// would not advice to do that, reason? Read below
result = new item.constructor();
} else {
result = item;
}
}
} else {
result = item;
}
}
return result;
}
function deleteData (date, elementToBeDel) {
let newObj = clone(x) // spreading to avoid mutating the object
newObj.dates.map(dt => {
if(Object.keys(dt)[0] === date){
if(dt[date].indexOf(elementToBeDel) > -1){
dt[date].splice(dt[date].indexOf(elementToBeDel), 1);
}
}
})
console.log("old Object", x)
console.log("new Object",newObj)
}
<button onclick="deleteData('7.01.2020', 3)">Click Me to delete</button>
I am facing a difficulty in obtaining the property value from a javascript class object.
The following is the class files and how I try to get its property value:
1: I defined a class named School in classes.js
export class School{
constructor()
{
this.schools = []; //to store schools returned from backend
}
//used to get all schools
getall()
{
axios.get('/api/schools')
.then(response =>{
this.schools = response.data;
console.error('School length: ' + this.schools.length);
this.ok = true;
//add origin data property
for(let i = 0; i < this.schools.length; i++)
{
this.schools[i]['orgValue'] = new tagChange();
}
})
.
catch(error => {
this.ok = false;
this.error = error.status;
this.schools = [];
});
return this.schools;
} //getall
}
2: I import this School class to another js file
//import School class
import {tagChange, School} from './classes.js';
export default {
components: {
modalAddSchool,
},
data() {
return {
schools: [], // my schools in
mySchoolObj: new School(), //create a School object
}
},
methods: {
getAllSchools()
{
//after this call, mySchoolObj's schools property indeed contain 8 elements in array format,
//However, this.schools can not be assigned successfully, so unexplainable
this.schools = this.mySchoolObj.getall();
//print zero element
console.log(this.mySchoolObj.schools);
//print zero element
console.log(this.schools)
}
},
3: after call getAllSchools() method, the mySchoolObj.schools indeed contain 8 school elements but this.schools cannot be assigned successfully, neither the following two console.log call can only print zero length
4: I really want to know how to return all the mySchoolObj.schools to this.schools, and how to get/visit its other property value?
axios.get is asynchronous, it means that when you return this.schools;, the ajax call is not finished yet so you return an empty array [].
More informations here:
Synchronous and asynchronous requests
You can return the promise given by axios or use a callback, like that:
//used to get all schools
getall(callback) { // take a callback function
axios.get('/api/schools')
.then(response =>{
this.schools = response.data;
console.error('School length: ' + this.schools.length);
this.ok = true;
//add origin data property
for (let i = 0; i < this.schools.length; i++) {
this.schools[i]['orgValue'] = new tagChange();
}
if (typeof callback === 'function') {
callback(this.schools, null); // call the callback with data and null (null because there is no error)
}
})
.catch(error => {
this.ok = false;
this.error = error.status;
this.schools = [];
if (typeof callback === 'function') {
callback(null, error.status); // call the callback with null data and the error status
}
});
return this.schools;
}
Then you can use your method like that:
methods: {
getAllSchools() {
this.mySchoolObj.getall((data, error) => {
if (error) {
return console.log(error);
}
this.schools = data;
console.log(this.schools);
});
}
},
(this code isn't tested, it may contain bugs)