How to Object.defineProperty on custom window object in Angular? - javascript

For instance I have YOTI object injected into browser:
When I run this in console:
window.YOTI
I get:
{onShare: ƒ, redirect: ƒ}
Then if I access redirect method I get this:
window.YOTI.redirect
ƒ (callbackUrl, token) {
var currentQuery = window.location.search.replace('?','')
var currentFragment = window.location.hash
var separator = (callbackU…
I want to modify this property in angular onInit() and I tried this from here(https://yoti.zendesk.com/hc/en-us/articles/360008958873-Does-the-Yoti-frontend-client-support-an-Angular-App-):
const ytcontainer = {
};
Object.defineProperty(window, 'YOTI', {
get: () => ytcontainer.YOTI,
set: yoti => {
ytcontainer.YOTI = yoti;
Object.defineProperty(yoti, 'redirect', {
get: () => {
return (callbackUrl, token) => {
console.log(callbackUrl);
console.log(token);
};
},
});
},
});
but after that, when I run window.YOTI I get undefined object.
window.YOTI
undefined
What am I doing wrong here?

Related

Why do these "get" functions return different results? [duplicate]

I try to define getter and setter in constructor via Object.assign:
function Class() {
Object.assign(this, {
get prop() { console.log('call get') },
set prop(v) { console.log('call set') },
});
}
var c = new Class(); // (1) => 'call get'
console.log(c.prop); // (2) => undefined
c.prop = 'change';
console.log(c.prop); // (3) => 'change'
Questions:
(1) Why getter is called?
(2) Why getter isn't called?
(3) Why setter is ignored?
The answer to all three of your questions is the same: Object.assign reads the value of the property from the source object, it doesn't copy getters/setters.
You can see that if you look at the property descriptor:
var source = {
get prop() { },
set prop(v) { }
};
console.log("descriptor on source", Object.getOwnPropertyDescriptor(source, "prop"));
var target = Object.assign({}, source);
console.log("descriptor on target", Object.getOwnPropertyDescriptor(target, "prop"));
To define that property on this inside Class, use defineProperty:
function Class() {
Object.defineProperty(this, "prop", {
get() { console.log('call get') },
set(v) { console.log('call set') },
});
}
var c = new Class();
console.log(c.prop); // => 'call get', undefined
c.prop = 'change'; // => 'call set'
console.log(c.prop); // => 'call get', undefined

TypeError: Cannot read property 'push' of undefined [VueJs]

I am trying to add an object to an array but it is not working with me, the program can't read the property push
I defined an array in <script>:
Data: function() {
return {
Projects: [
{
name: '',
id: 0,
subscribers: 0,
products: {name:'',color:''},
}
],
}
And in the function:
GetAllWorkspaces: function(){
var app = this;
const instance = axios.create({
timeout: 1000,
headers: {
........
}
});
instance.get("XXXXXXX")
.then( function(response) {
console.log(response);
Object.keys(response.data.result).forEach( function (product) {
var subscribersCounter = 0;
let example = {
name: response.data.result[product].name,
id: response.data.result[product].id,
subscribers: response.data.result[product].subscribers,
products: response.data.result[product].products,
};
let uploadedExample = {
name: '',
id: '',
subscribers: '',
products: {name:'',color:''},
};
uploadedExample.name = example.name;
uploadedExample.id = example.id;
if ( example.subscribers ) {
Object.keys(example.subscribers).forEach(function (key) {
subscribersCounter++;
});
}
uploadedExample.subscribers = subscribersCounter;
if ( example.products ) {
Object.keys(example.products).forEach(function (Pkeys) {
uploadedExample.products.name = Pkeys;
Object.keys(example.products[Pkeys]).forEach(function (key) {
if (key == 'color') {
uploadedExample.products.color = example.products[Pkeys][key];
}
});
});
}
//add the new workspace to the list of workspaces.
app.Projects.push(uploadedExample);
});
})
.catch(function(error) {
console.log(error);
});
My problem is with this line
app.Projects.push(uploadedExample);
where when I try to push an object into the array, the error message is shown:
TypeError: Cannot read property 'push' of undefined
As the error says, the problem is that app.Projects is undefined. This happens because 'this' refers to the function scope inside GetAllWorkspaces and not to the component scope (you can try it by console.logging 'this' - anyway- it is a good practice under all circumstances because 'this' can change from context to context). If you want to keep the component scope inside the method, you should use an arrow function like this:
GetAllWorkspaces: () => {
// do all your stuff
}

Is it possible to modify defined getter function?

Working on a performance reviewing tool on wechat mini apps platform (javascript + native hybrid based on wechat app), I am trying to inject codes into its prototypes, for example the wx.request function.
This is how you would use a wx.request function:
wx.request({
url: 'test.php',
data: {
x: '' ,
y: ''
},
header: {
'content-type': 'application/json'
},
success: function(res) {
console.log(res.data)
}
})
So in order to know how long the request has taken without manually writing adding all the anchors, I tried to inject code by:
var owxrequest = wx.request
wx.request = function() {
console.log('test', Date.now())
return owxrequest.apply(owxrequest, arguments)
}
This failed and I got an Cannot set property "prop" of #<Object> which has only a getter error.
So I realized the the object must have been defined similar to:
wx = {
request: get function(){
...
}
...
}
So I tried:
var owxrequest = wx.request
Object.defineProperty(wx, 'request', {
get: function() {
console.log('test', Date.now())
return owxrequest.apply(owxrequest, arguments)
}
})
This failed with an error (request: fail parameter error: parameter.url should be String instead of Undefined). Then I tried:
var owxrequest = wx.request
Object.defineProperty(wx, 'request', {
set: function() {
console.log('test', Date.now())
return owxrequest.apply(owxrequest, arguments)
}
})
This wouldn't throw an error but it also has no effect when calling wx.request()...
You can implement this by re-define the getter. The point is: the re-defined getter should return a function object, as wx.request is a function:
Object.defineProperty(wx, 'request', {
get: function() {
return function() {
//...
};
}
});
Why I get the error: request: fail parameter error: parameter.url should be String instead of Undefined?
You are trying to access the arguments of the getter itself (the arguments of function in get: function(){...}). This arguments is an empty object and it can be verified by console.log() statement. As it is empty, arguments.url is undefined, that's why wx complains about the parameter.
Here is an working example:
let wx = {
get request(){
return function() {
console.log(10);
return 88;
};
}
};
let oldF = wx.request;
Object.defineProperty(wx, 'request', {
get: function() {
return function() {
console.log(new Date());
return oldF.apply(wx, arguments);
};
}
});
console.log(wx.request());
The above code would print:
2017-08-28T06:14:15.583Z // timestamp
10
88
You could just shadowing the request function.
Simple example:
Shadowing the getter:
// original getter latest
let base = {
num: 1,
get latest() {
return this.num;
}
}
console.log(base.latest);
// shadowing getter latest
Object.defineProperty(base, 'latest', {
get: function() {
return this.num + 1;
}
});
console.log(base.latest);
Simple shadowing a object property
// your original object
let base = {
request(){
console.log('request original');
}
};
base.request()
base.request = () => {
console.log('request new implementation');
};
// now shadow the original request implementation
base.request()

Jasmine testing setting component variable

I'm using spyOn, in a jasmine test, to "listen" to a function call from a service,
that function returns an Observable.
I'm getting the error unexpected token U JSON;
The error is generated from the component line:
this.config = JSON.parse(localStorage.getItem('configuration'));
The localStorage item was JSON.stringified;
I understand that this error is usually thrown when JSON.parse = undefined,
So I tried to set the variable within my test i.e.
component.config = mockConfig;
So..
// Storage Mock
function storageMock() {
var storage = {};
return {
setItem: function(key, value) {
storage[key] = value || '';
},
getItem: function(key) {
return key in storage ? storage[key] : null;
},
removeItem: function(key) {
delete storage[key];
},
get length() {
return Object.keys(storage).length;
},
key: function(i) {
var keys = Object.keys(storage);
return keys[i] || null;
}
};
}
let mockConfig = JSON.stringify({
base_url:"http://image_url/",
poster_sizes:['w9', 'w100']
})
//Set storage
let m = storageMock()
m.setItem('configuration', mockConfig)
it('Should set items array with values from MoviesService', () => {
component.config = JSON.parse(m.getItem('configuration'));
let spy = spyOn(moviesService, 'getPreview').and.callFake(()=>{
return Observable.from([[{id1: 1, title: 'a'}, {id1: 2, title: 'b'}]])
})
component.ngAfterViewInit();
expect(component.items.length).toBeGreaterThan(0);
});
For anyone who come across this problem I got this working by by placing this code in the beforeEach wrapper
Object.defineProperty(window, 'localStorage', { value: m });
Basically it uses my mock localStorage variable instead of the one from the window object.

Console logging "this" returns "null"

I am trying to create a flux store for a React app I am building. I am using an object-assign polyfill npm package and Facebook's Flux library.
Initially I was getting the error "Cannot read property '_data' of null' error in the console which was refering to var currIds = this._data.map(function(m){return m.id;});. That method is currently the only one being called directly. I then did console.log(this) which returned "null".
I find this strange. What is going on?
My code:
var Assign = require('object-assign');
var EventEmitterProto = require('events').EventEmitter.prototype;
var CHANGE_EVENT = 'CHANGE';
var StoreMethods = {
init: function() {},
set: function (arr) {
console.log(this);
var currIds = this._data.map(function(m){return m.id;});
arr.filter(function (item){
return currIds.indexOf(item.id) === -1;
}).forEach(this.add.bind(this));
},
add: function(item){
console.log(this);
this._data.push(item);
},
all: function() {
return this._data;
},
get: function(id){
return this._data.filter(function(item){
return item.cid === id;
})[0];
},
addChangeListener: function(fn) {
this.on(CHANGE_EVENT, fn);
},
removeChangeListener: function(fn) {
this.removeListener(CHANGE_EVENT, fn);
},
emitChange: function() {
this.emit(CHANGE_EVENT);
},
bind: function(actionType, actionFn) {
if(this.actions[actionType]){
this.actions[actionType].push(actionFn);
} else {
this.actions[actionType] = [actionFn];
}
}
};
exports.extend = function(methods) {
var store = {
_data: [],
actions: {}
};
Assign(store, EventEmitterProto, StoreMethods, methods);
store.init();
require('../dispatcher').register(function(action){
if(store.actions[action.actionType]){
store.actions[action.actionType].forEach(function(fn){
fn.call(null, action.data);
})
}
});
return store;
};
I can't see where set is called, however your this can be null if the function is invoked through call (see here) or apply, and your first argument is null.
This also happens in your require.register callback:
fn.call(null, action.data) //First parameter is your 'this'.

Categories