I have a Vue(2.5+) component where I'm setting a data property to a new Foo object. Using foo.bar() in the click handler calls the method correctly, but throws Uncaught TypeError: cannot set property 'someVariable' of null when trying to modify properties inside the Foo class. Setting it up so that Foo is an object literal instead of a class also does not resolve the error.
I suspect something weird is happening with this, between the component and the class?
Vue component
import Foo from './foo.js'
export default {
template: `<div #click="foo.bar"></div>`,
data() {
return {
foo: new Foo()
}
},
created() {
console.log(foo); // foo is not null here
}
}
Foo class
export default class Foo
{
constructor()
{
this.someVariable = 0;
}
bar(e)
{
// modify this.someVariable
}
}
but if I change the vue component to reference the external method through it's own "methods" property, it works.
Vue component (working)
import Foo from './foo.js'
export default {
template: `<div #click="bar"></div>`,
data() {
return {
foo: new Foo()
}
},
methods: {
bar(e) {
this.foo.bar(e);
}
}
}
As said in the comments, foo.bar without any context attached to it :
In JS functions are objects, just like any object they have their own this "pointer".
In the evaluation of their body, this is bound to a specific object referred to as context which is either the default context (automatically set) or user defined (manually set).
Inheritance in JS is achieved through a prototype chain and methods should be defined on/attached to the class's prototype. Because of this, when you call foo.bar() :
You are in a method call context, therefore foo will be bound to the method
bar is searched on the object first then in the prototype chain
But methods behave just like any other property : when you do foo.bar you get a reference to the actual method which is an unbound function (default behavior for methods, since it is bound when called on an object).
Therefore, what you really need to do in this situation is foo.bar.bind(foo).
I would also suggest taking a quick look into this ES6 proposal for a bind operator and its implementation as a Babel plugin which allows nice things like passing ::foo.bar instead of foo.bar.bind(foo)
Related
I want to know what is the difference in VueJs between these two types of declaration :
data() {
return {
foo = 'bar'
}
}
and this :
created() {
this.foo = 'bar'
}
I know that I an access both using 'this' in or in methods.
Also, if the returned object in data() is saved in "memory" of the component, where does the object declared in created() is saved? are they in different scopes?
Also, I know that to fetch data, the fetcher lands in created() and then updates the data object, but this question is specifically about the differences between the two ways of declarations i mentioned
Is there any differences in the background?
You can read more about vue data here.
Vue will recursively convert its ($data) properties into getter/setters to make it “reactive”..
created() {
this.foo = 'bar'
}
Declaring like above, you won't be able to watch the attributes.
You can check this example out, watch function isn't fired with the attribute being not defined in data()
There are couple of questions related computed properties like the following
"vuejs form computed property"
"Computed properties in VueJs"
"computed property in VueJS"
"Use computed property in data in Vuejs"
They are asking about specific error or logic. There are lot of websites that are explaining about vuejs related concepts. I read about computed properties on vuejs official website. When we do complex calculations or want to avoid to write more logic in our html template then we use computed properties.
But could not get any solid understanding about computed properties, when it calls, how it calls, what exactly do?
TL;DR: Computed properties are getters/setters in Vue.
When defined in the shorthand form, they are getters:
computed: {
someComputed() {
return `${this.foo} ${this.bar}`;
}
}
is equivalent with
computed: {
someComputed: {
get: function() {
return `${this.foo} ${this.bar}`;
}
}
}
which can also have a setter:
computed: {
someComputed: {
get: function() {
return `${this.foo} ${this.bar}`;
}
set: function(fooBar) {
const fooBarArr = fooBar.split(' ');
this.foo = fooBarArr[0];
this.bar = fooBarArr[1];
}
}
}
In short, Vue computed properties allow you to bind an instance property to
a getter: function run when you look up that property; usage:
this.someComputed // returns the computed current value, running the getter.
a setter: function run when you attempt to assign that property; usage:
this.someComputed = value; // sets the computed current value, running the setter.
Read more on getters and setters in Javascript.
And here's the documentation on Vue computed properties.
You can use computed properties when for example you have some logic what will blow up your template.
The idea is, that normally you want to keep all javascript logic in the javascript side of your vue component, and only access final data in your data (if possible)
For that you can use computed props, which normally are doing simple things like:
computed: {
// a computed getter
reversedMessage: function () {
// `this` points to the vm instance
return this.message.split('').reverse().join('')
}
}
Or an another good example if you have some currency and you want to format it with thousand separator and euro ign at the end.
Then you can access your computed prop in the template like you access a normal prop, you dont have to call it as a function.
like so:
<div>{{reversedMesage}}</div>
Every time, when any variable what is used in your conputed prop is changing, vue vill take care of it and will re-calculate your computed property again.
Lets say you have the following:
computed: {
prettyAmount: function () {
return this.amount + ' ' + this.currency.toUpperCase()
}
}
<div>{{prettyAmount}}</div>
Whenever currency or amount changes, the output of prettyAmount will be changed as well.
The setup is really simple, assume this piece of code:
export default class App extends WidgetBase {
protected render() {
return v('div', [
w(MyCustomWidget, {}),
v('button', {
id: 'abc',
classes: ['btn', 'btn-primary'],
onclick: this.clickMe
}, [
'Hello World!'
])
]);
}
}
The class MyCustomWidget defines now a function which I want to call from the current App-widget. If I do let cw = w(MyCustomWidget, {}) I get an object with the key instance which contains exactly what I want. But if I use cw.instace TypeScript tells me, that Property instance does not exist on type 'WNode<MyCustomWidget>'.
So how to do it properly?
I reached out to the guys from Dojo2 and they responded to me very quickly with:
If you want a child widget to call a function from the parent you need pass it to the child as a property. In dojo the widget instance are never exposed.
This was also my workaround but I was not sure if this was the correct way to do it. It certainly works. Now we know.
I've created a simple custom element in Aurelia that uses an #inlineView() (because the view is tiny) but when I try to access one of my VM's properties from my inline view I just get "property is not defined";
import {inlineView} from 'aurelia-framework';
#inlineView(`<template><h1>${title}</h1></template>`)
export class MyCustomElement {
constructor () {
this.title = 'Hello, World!';
}
}
This happens with #bindable as well;
export class MyCustomElement {
#bindable title = 'Hello, World!';
constructor () {
}
}
When the <template><h1>${title}</h1></template> is being interpreted, the interpreter tries to interpolate the title variable which does not exist yet. Try this:
#inlineView(`<template><h1 innerHTML.bind="title"></h1></template>`)
Or even easier:
#inlineView('<template><h1>${title}</h1></template>') // without accents
The problem is you are using a template literal in your inlineView decorator which is causing Javascript to evaluate ${title} before it gets passed to the inlineView decorator function. At that point, title does not exist. You need to pass a regular string in this instance using regular quotes (' or ") around the template string like so:
#inlineView("<template><h1>${title}</h1></template>")
I'm making use of the extjs class objects through Ext.define (... and Ext.create (.... When I have multiple instances of classes stored within another class I'm seeing some strange behavior: the classes are not unique and it looks like Ext.create is returning my previous instantiation.
Checkout the JSFiddle of my problem here. Make sure you view the console log in your browser to see the output and weirdness.
You're setting an array in Ext.define. That implies that you're setting into the object's prototype which is shared among all instances of a class. Therefore this is not an unexpected behaviour. Create the array within the constructor, like here:
Ext.define ('Sunglasses', {
brand : '',
constructor : function (args) {
this.lenses = [];
this.brand = args.brand;
},
addLenses : function (lenses) {
this.lenses.push (lenses);
}
});