Why is data value undefined inside of 'Event Bus'? [duplicate] - vuejs2

This question already has answers here:
Methods in ES6 objects: using arrow functions
(6 answers)
Closed 6 years ago.
First I tried this -
const profile = {
name: 'Alex',
getName: function(){
return this.name;
}
};
Which works fine. Now I tried the same thing with fat arrow. In that case "this" is coming undefined.
const profile = {
name: 'Alex',
getName: () => {
return this.name;
}
};
This gives me an error
TypeError: Cannot read property 'name' of undefined
What I learned was, fat arrow syntaxes are way better handling implicit "this". Please explain why is this happening.

Unlike regular functions, Arrow functions does not have a this of their own, only regular functions and global scope have this of their own.
Which would mean that whenever this would be referred in arrow function, it will start looking up the scope to find the value of this, or in this case, during lookup it found, that the object is not having a this of its own, hence, it went up to global scope and bound the value of this with global scope, where it won't find anything. These two examples will solve your doubt.
var obj = {
a : 'object???',
foo : () => { console.log(this.a) }
};
var a = 'global!!!';
obj.foo(); // global!!!
Wrapping arrow within a function
var obj = {
a : 'object???',
foo : function() {
return (() => {
console.log(this.a)
})();
}
};
var a = 'global!!!';
obj.foo();
Here, I have tried to explain the behaviour of this for arrow in depth.
https://github.com/anirudh-modi/JS-essentials/blob/master/ES2015/Functions/Arrow%20functions.md#how-this-is-different-for-arrow-functions

Related

chai throw with property deep equal

expect.to.throw returns a Proxy to the thrown Error, so I can use with.property in order to check some properties of the error.
I attach a details object on my custom errors but I can't test for them, since the with.property compares only using strict equals.
Can I compare this property using deep equal somehow?
Example:
class DocNotFoundError extends Error {
constructor(message, docId) {
super(message)
this.details = { docId }
}
}
const getDoc = id => {
throw new DocNotFoundError('errors.docNotFound', id)
}
const docNotFound = expect(() => getDoc('01234567890')).to.throw('DocNotFoundError')
docNotFound.with.property('details', { docId: '01234567890' }) // fails
The error will fail similar to
AssertionError: expected { Object (error, ...) } to have property 'details' of { Object (docId) }, but got { Object (docId) }
+ expected - actual
I assume this is due to it only checks for reference equality and not deep object equality.
First of all there is a typo in the question: DocNotFoundError should not be in quotes.
I managed to get it working with docNotFound.to.have.deep.property('details', { docId: '01234567890' }), so yes you should perform deep comparison to check if objects have keys with same values.
Source 1
Source 2

How to execute strings of expression in an array with Ramda

I've got an array where strings of expression are listed.
const newPIXI = ["container1 = new PIXI.Container();","container2 = new PIXI.Container();","container3 = new PIXI.Container();"]
I managed to run this with (Function(...newPIXI))()
How can I do this with Ramda?
I tried R.forEach , but didn't work.
These are strings, and not functions. To run them you need to evaluate them using eval() (or Function and then run them). Each string is an expression, and not an actual function. Running the expression will create global variables (container1, container2, and container3).
You've probably heard that eval() is evil. Using eval() is a security risk, and hurts performance, and Function is only slightly less so (read more here):
eval() is a dangerous function, which executes the code it's passed
with the privileges of the caller. If you run eval() with a string
that could be affected by a malicious party, you may end up running
malicious code on the user's machine with the permissions of your
webpage / extension. More importantly, a third-party code can see the
scope in which eval() was invoked, which can lead to possible attacks
in ways to which the similar Function is not susceptible.
Function's advantage is that it runs in the global scope, and can't access variables in the scope it was called in. However, Function still allows running arbitrary code.
Eval example:
const PIXI = {
Container: function () {
this.example = 'Container';
}
};
const newPIXI = ["container1 = new PIXI.Container();","container2 = new PIXI.Container();","container3 = new PIXI.Container();"]
newPIXI.forEach(x => eval(x))
console.log({ container1, container2, container3 });
Function example:
const PIXI = {
Container: function () {
this.example = 'Container';
}
};
const newPIXI = ["container1 = new PIXI.Container();","container2 = new PIXI.Container();","container3 = new PIXI.Container();"]
newPIXI.forEach(x => Function(x)())
console.log({ container1, container2, container3 });
It's better to pass data that tells the browser what you want to do (a command), and not how to do it. Then in the code you can decide how to interpret the command. For example:
const PIXI = {
Container: function () {
this.example = 'Container';
}
};
const newPIXI = [{ type: 'PIXIContainer', name: 'container1' }, { type: 'PIXIContainer', name: 'container2' }, { type: 'PIXIContainer', name: 'container3' }]
const result = {};
newPIXI.forEach(({ type, name }) => {
switch(type) {
case 'PIXIContainer':
result[name] = new PIXI.Container();
break;
default:
throw new Error(`Type ${type} not found`);
}
})
console.log(result);

Watch for variable change do not work

I need to check variable rasters_previews_list for changing. Here is my code:
var userContent = Vue.extend({
template: '<p>Some template</p>',
data: function () {
return {
rasters_previews_list: []
}
},
watch: {
'rasters_previews_list': function(value, mutation) {
console.log("Value changed");
}
}
});
But In console I do not see Value changed when it got new data.
Data changing function:
map.on('draw:created', function (e) {
//...
Vue.http.post('/dbdata', DataBody).then((response) => {
userContent.rasters_previews_list = response; // putting JSON answer to Component data in userContent
console.log(response);
}, (response) => {
console.log("Can't get list rasters metadata from DB. Server error: ", response.status)
});
I change value in map.on('draw:created', function (e) (Leaflet JS). I see console.log output, so seems data is changing.
If you want to change the value of an array you will have to use the special Array extension methods Vue.set and Vue.delete.
Due to limitations of JavaScript, Vue cannot detect the following changes to an Array:
When you directly set an item with the index, e.g. vm.items[0] = {};
When you modify the length of the Array, e.g. vm.items.length = 0.
https://vuejs.org/api/#Vue-set
This problem is also mentioned in the common gotchas
When you modify an Array by directly setting an index (e.g. arr[0] = val) or modifying its length property. Similarly, Vue.js cannot pickup these changes. Always modify arrays by using an Array instance method, or replacing it entirely. Vue provides a convenience method arr.$set(index, value) which is just syntax sugar for arr.splice(index, 1, value).

Vuejs deep nested computed properties

I'm not really understanding where to put function() { return {} } and where not to when it comes to deeply nesting computed properties.
By the way, this is in a component!
computed: {
styles: function() {
return {
slider: function() {
return {
height: {
cache: false,
get: function() {
return 'auto';
}
},
width: {
cache: false,
get: function() {
return $('#slideshow').width();
}
}
}
}
}
}
},
This is returning undefined. When I get rid of the function() { return {} } inside of the slider index, it returns an object when I do styles.slider.width instead of the get() return. It just shows an object with cache and get as indexes..
Thanks for any help!
The reason I'm asking is because I have multiple nested components that involve styling from the parent. Slider, tabs, carousels, etc. So I wanted to organize them like this.
I believe you mean to return a computed object, but not actually structure the computation in a nested manner?
What the others have said regarding the 'computed' hook not having syntax for nesting is correct, you will likely need to structure it differently.
This may work for you: I generate many objects in a similar fashion.
computed: {
computedStyles(){
var style = {slider:{}}
style.slider.height = 'auto'
style.slider.width = this.computedSlideshowWidth
return style
},
computedSlideshowWidth(){
return $('#slideshow').width()
}
As per 2020 and Vue 2.6.12 this is completelly possible. I believe this has been possible since v.2 but cannot confirm.
Here is the working example:
this.computed = {
// One level deep nested,
// get these at `iscomplete.experience`
// or `iscomplete.volume`
iscomplete: function() {
return {
experience: this.$data.experience !== null,
volume: this.$data.volume > 100,
// etc like this
};
},
// More levels deep nested.
// Get these at `istemp.value.v1 and `istemp.value.v2`
istemp: function() {
return {
value1: {
v1: this.$data.experience,
v2: 'constant'
}
}
}
};
As a result you will be able to access your deep nested computed in your template as e.g. follows <span v-text="iscomplete.experience"></span> that will output <span>true</span> for the first example computed above.
Note that:
Since Vue v.2 cache key is deprecated;
Vue would not execute functions assigned to a computed object nested keys;
You cannot have computed for non-Vue-reactive things which in your case is e.g. $('#slideshow').width(). This means they are not going to be re-computed on their content change in this case (which is a computed's sole purpose). Hence these should be taken away from computed key.
Other than that I find nested computeds to be quite helpful sometimes to keep things in better order.

Jquery dyamically inserting function pointers into event listeners

Using Jquery, I would like to dynamically generate a listener and the actions to be carried out by each listener.
The problem is that the pointer to the dynamically chosen function, below func[i], is never replaced by the the content that it represents (in this case any of the functions stored in the func array). It seems clear that this is happening because JS doesn't go inside the function(){...} until the listened for click takes place.
I'm not sure of how to solve the problem and any thoughts would be greatly appreciated. Below is some sample code and if you like here is a jsfiddle. Many thanks!
// an array of functions to call and a random number to choose which one to call
var func = ["Alf();","Sarah();","Jon();"];
var i = Math.floor( Math.random() * 3 );
// the dynamically generated listener
$('#foo').on('click', function() {
func[i];//pointer to function
});
// a function to be called
function Alf () {
alert("Hi! I'm Alf");
}
You need to change your array to function references not strings:
var func = [Alf, Sarah, Jon];
And, change your execution of it to:
// the dynamically generated listener
$('#foo').on('click', function() {
func[i]();//pointer to function
});
And, you'll need to make sure that your variable i retains its value until the event actually occurs and i is a dangerous variable name to use for that.
I'd suggest this:
(function() {
// an array of functions to call and a random number to choose which one to call
var funcArray = [Alf, Sarah, Jon];
var randomFunc = funcArray[Math.floor( Math.random() * func.length )];
// the dynamically generated listener
$('#foo').on('click', function() {
randomFunc(); //pointer to function
});
// a function to be called
function Alf () {
alert("Hi! I'm Alf");
}
})();
Changes:
Put the whole thing in an IIFE so your variables are protected and not global
Get the actual random function rather than just the index
Use func.length instead of 3 for maintainability.
Use a better name for the random function variable
Change functions to function references instead of strings.
Add parens to end of randomFunc() to actually execute the function
If you want to make this into a function that allows you to use the logic in multiple places, you can do something like this:
function bindRandomClickFunc(selector, fnArray) {
var fn = fnArray[Math.floor( Math.random() * fnArray.length )];
$(selector).on('click', fn);
}
Part of the problem is that when you call func[i] you are just calling a string. Instead of making func an array of strings, consider making it an array of function references, then invoking your function with ()
// a function to be called
function Alf () {
alert("Hi! I'm Alf");
}
function Sarah () {
alert("Hi! I'm Sarah");
}
function Jon () {
alert("Hi! I'm Jon");
}
// an array of functions to call and a random number to choose which one to call
var func = [Alf, Sarah, Jon];
var i = Math.floor( Math.random() * 3 );
// the dynamically generated listener
$('#foo').on('click', function() {
func[i](); //pointer to function
});
JSFiddle: http://jsfiddle.net/tuanderful/8f5fZ/1/
use this
$('#foo').on('click', function()
{
var i = Math.floor( Math.random() * 3 );
eval(func[i]);//pointer to function
});