How to do computed values in Coffeescript with MobX 4? - coffeescript

Using MobX 3 in Coffeescript, I could do this:
mobx = require 'mobx'
state = mobx.observable
items:['a','b']
compute = (key,fn)->
obj = {}
obj[key] = mobx.computed fn.bind state
mobx.extendObservable state, obj
compute 'num', ->
#items.length
mobx.autorun ->
console.log "items = #{state.items}"
mobx.autorun ->
console.log "num = #{state.num}"
state.items.push 'c'
...which works as expected,
but with MobX 4 that same codes gives the error:
Error: [mobx] Passing a 'computed' as initial property value is no
longer supported by extendObservable. Use a getter or decorator
instead
All of the examples that I can find use Javascript with Classes and Decorators and Getters.
But Coffeescript does not really do "decorators" or "getters", and I would prefer not to use Classes ( I am old-school )
So...how can I create a computed "num" on my "state"?
Can my "compute()" function be altered to make it all work again?

Ok, I figured-out how to do it, and since there have been no answers....here it is:
compute = (key,fn)->
Object.defineProperty observable_state, key,
get: fn
configurable: true
obj = {}
obj[key] = mobx.computed
mobx.decorate observable_state,obj
First, you create a configurable "getter" (it must be configurable because MobX is going to re-configure it), then you call the "decorate" function ( instead of using ES6 decorators ).

Related

Coffeescript classes and scope

While learning coffeescript, I'm trying to create an instance of class A inside a method of class B, this is the code:
class #A
constructor: (#x) ->
show: ->
alert #x
class #B
constructor: (#y) ->
show: ->
a = new #A("eric")
alert a.x
alert #y
b = new #B("daniel")
b.show()
the error is TypeError: undefined is not a function.
Any help is appreciated.
Thanks
You have two problems:
# is just another way of saying this in CoffeeScript. That's all it means.
Classes are (more or less) just variables or properties like any other in CoffeeScript.
So when you say #A, you're just looking for the A property of this and your show is really saying:
a = new this.A("eric")
In that context, # will be an instance of B and Bs don't have A properties. Instead you should just say:
a = new A('eric')
Using # when defining a class:
class #A
#...
is just a way to make a class globally available. At the top level, # will (almost always) be window in a browser so you're really saying:
class window.A
#...
and window properties are globals. Keep in mind that each CoffeeScript file is wrapped in a function when it is converted to JavaScript:
Although suppressed within this documentation for clarity, all CoffeeScript output is wrapped in an anonymous function: (function(){ ... })(); This safety wrapper, combined with the automatic generation of the var keyword, make it exceedingly difficult to pollute the global namespace by accident.
So if you just said:
class A
then A would only be available to other code in that file. Saying:
class #A
makes A global.
If you're only working with one file then you don't need the #s on your classes:
class A
constructor: (#x) ->
show: ->
alert #x
class B
constructor: (#y) ->
show: ->
a = new A("eric")
alert a.x
alert #y
b = new B("daniel")
b.show()
Don't get in the habit of prefixing everything with #, only use it on classes when you need it and you know exactly what it will do. Even when you need it, there are better ways: use require.js to manage your dependencies, use an global application-specific object to manage scopes, ...

How to use set methods to initialize instance variables via constructor in coffeescript

I have a "class" in coffee script whose instance variables I want to initialize with instance methods that return a value via a callback, but it doesn't work as I had hoped:
EventEmitter = require('events').EventEmitter
class MyClass extends EventEmitter
constructor: ->
#datamember: setDatamember()
setDatamember: ->
someFunction (response) ->
#datamember = response
#emit 'init'
getDatamember: ->
return #datamember
----
myObj = new MyClass
myObj.on 'init', ->
console.log myObj.getDatamember
The result I get suggests that "this" in setDatamember is referring to something different from what "this" refers to in the object instance. If I explicitly call myObj.setDatamember, I get the expected result, but is there any way to call on a set method -- specifically one that sets the data member via a callback -- in the constructor? I've looked through the docs, as well as various other sources of coffeescript info (e.g. this one), and I haven't found anything that touches upon this.
Try using a fat arrow for the anonymous function:
setDatamember: ->
someFunction (response) =>
#datamember = response
#emit 'init'
Also, you'll need to call the correct function in the constructor:
constructor: ->
#setDatamember()
In general, avoid fat arrows on methods - the way Coffee-Script implements this does some bad things to memory usage. Also, it will rarely be necessary.
However, anonymous functions that refer to this will almost always need fat arrows. this is not held in closure like normal variables, and will only be set by binding (Function.prototype.bind) or by calling it as an object method (obj.myMethod() will set this to obj in myMethod).
Try using fat arrows on everything except the constructor:
class MyClass
constructor: ->
#setDatamember()
setDatamember: =>
someFunction (response) =>
#datamember = response
getDatamember: =>
return #datamember
However, you also look to have someFunction in there as an asynchronous function, so you'll never be able to just do
mc = new MyClass
console.log mc.datamember
Because that doesn't wait for someFunction to return before accessing mc.datamember.

Correct way to return function value instead of binding in Coffeescript

I can't seem to find a concise answer to this question. What is the correct coffeescriptic way to return the value from _otherInstanceMethod when calling #_instanceMethod instead of the function binding itself?
x = _instanceMethod: () ->
#_otherInstanceMethod key: 'value'
Edit (thanks commenters)
This returns:
x = function () {
[...] # function body omitted
});
Instead of
x = 'some value returned by _otherInstanceMethod'
I would like the value to be returned instead of the function binding to _otherInstanceMethod
Being totally new to Coffeescript, this was my fault. I was calling the instance method like:
#_instanceMethod
instead of
#_instanceMethod()
Sorry for the trouble, voting to delete
In CoffeeScript #something translated into this.something regardless of the underlying variable type. This means you can use # only in conjuction with properties, with methods you still ought to use good old this.

When does the "fat arrow" (=>) bind to "this" instance

The fat arrow can be used in different settings but it somehow doesn't
always bind to the instance I want.
The fat arrow binds at 3 occasions
when declaring a method
when declaring a function within a method
when declaring a function in global context
1. When declaring a method
When the Coffeescript compiler encouters the following syntactical pattern
within a class declaration
class A
somemethod: (paramlist) =>
This will yield the following code within the constructor of class A
this.somemethod = __bind(this.somemethod, this);
That is the definition for that instance is overwriting the initial assignment
with a bound version of the function
2. When declaring a function within a method
When you define a function with a fat arrow within a method the Coffeescript compiler
automatically creates a closure and and shadows this of the outer method into the variable
_this. Any reference to # within the inner function will use the variable _this
in the generated javascript code
somemethod: ->
=> #someCall()
And this is the corresponding Javascript
A.prototype.somemethod = function() {
//_this references this
var _this = this;
return function() {
//and _this is now used within the inner function
return _this.someCall();
};
};
A definition of a function without the fat arrow doesn't create that closure for you.
3. When declaring a function in global context
If you define a free floating function (meaning as a method in a class and not within another function/method) just like this
foo = => #bar
Then the corresponding Javascript will look like this
var foo,
_this = this;
foo = function() {
return _this.bar;
};
The interesting thing here again is that this is being assigned to _this which enables the definition of foo to close over _this.
The important part however is that this is always the global context of your execution environment. If you are in the browser it will be the window object. If you are running node.js it will be the module you are just running.
Warning: You shouldn't define any function accessing your global context anyway. This calls for trouble.

Why must my coffeescript method belong to the class?

I come from a C#/Java background which use a class based OO system and I don't get the JavaScript/CoffeeScript prototype OO system yet. I've written a CoffeeScript class below which allows me to display names for contacts according to a system-side preference. I can only get the class to work by making the joinNonEmpty(stringList, joinText) method belong to the prototype and calling it the way I would call a static method in Java/C# land.
Is there a way I can make this method call using this.joinNonEmpty(...)?
Can you shed some light on why I can reference the firstLastRender, lastFirstRender and firstOrNickThenLast methods in the constructor with this. but it doesn't work from those methods when calling the joinNonEmpty helper?
Does this have something to do with how I'm locating the appropriate method via the preference map?
prefs = displayNameFormat: "FirstOrNickThenLast"
class DisplayNameRenderer
constructor: ->
#prefToRenderMap =
FirstLast: this.firstLastRender
LastFirst: this.lastFirstRender
FirstOrNickThenLast: this.firstOrNickThenLast
# Why does this method have to be static (a class method)?
#joinNonEmpty: (stringList, joinText) ->
nonEmptyStrings = []
for s in stringList
nonEmptyStrings.push(s) if s isnt null and s isnt ""
nonEmptyStrings.join(joinText)
firstLastRender: (contact) ->
# TypeError: Object expected.
joinNonEmpty([contact.firstName, contact.lastName], ' ')
lastFirstRender: (contact) ->
# TypeError: Object doesn't support this method or property
this.joinNonEmpty([contact.lastName, contact.firstName], ', ')
firstOrNickThenLast: (contact) ->
# Works correctly.
DisplayNameRenderer.joinNonEmpty([(if contact.nickname isnt null and contact.nickname isnt "" then contact.nickname else contact.firstName), contact.lastName], ' ')
render: (contact) ->
#prefToRenderMap[prefs.displayNameFormat](contact)
contact = firstName: "Jonathan", nickname: "Jonny", lastName: "Appleseed"
dnr = new DisplayNameRenderer()
# => "Jonny Appleseed"
console.log dnr.render(contact)
Thanks for taking the time to answer.
this (AKA #) is determined when the function is called (with exceptions as below). So when you do this:
#prefToRenderMap =
FirstLast: this.firstLastRender
LastFirst: this.lastFirstRender
FirstOrNickThenLast: this.firstOrNickThenLast
You're storing unbound references to the three functions in the #prefToRenderMap instance variable and #prefToRenderMap is itself an object. Then you try to call the methods in your DisplayNameRenderer instance like this:
#prefToRenderMap[prefs.displayNameFormat](contact)
and everything falls apart because the methods are called in the wrong context and # isn't what they're expecting it to be. If prefs is 'FirstOrNickThenLast' then you're effectively doing this:
#prefToRenderMap.FirstOrNickThenLast(contact)
and # (AKA this) will be #prefToRenderMap inside the firstOrNickThenLast method. But, of course, #prefToRenderMap doesn't have any of the methods that you're trying to call so you get various errors.
One solution is to use the fat arrow (=>) to define the methods:
The fat arrow => can be used to both define a function, and to bind it to the current value of this, right on the spot.
So you could have things like this:
joinNonEmpty: (stringList, joinText) ->
#...
firstLastRender: (contact) =>
#joinNonEmpty([contact.firstName, contact.lastName], ' ')
and everything will work out. Here's a stripped down demo that will also show you your this problem:
http://jsfiddle.net/ambiguous/RAPJw/1/
You could also avoid this problem by referring to the methods by their names. Given a method name in a string, m = 'some_method', you can call that method like this o[m]() in both JavaScript and CoffeeScript and the result will be the same as if you said o.some_method(). A better structure would look more like this:
class DisplayNameRenderer
constructor: ->
#prefToRenderMap =
FirstOrNickThenLast: 'firstOrNickThenLast'
joinNonEmpty: (stringList, joinText) ->
#...
firstOrNickThenLast: (contact) ->
#joinNonEmpty([(if contact.nickname isnt null and contact.nickname isnt "" then contact.nickname else contact.firstName), contact.lastName], ' ')
render: (contact) ->
#[#prefToRenderMap['FirstOrNickThenLast']](contact)
Note the change to the structure of #prefToRenderMap and how it is used in render. And a demo of this approach: http://jsfiddle.net/ambiguous/DFYwu/
As an aside, instead of saying ClassName.class_method() inside an instance method, you can use the constructor property instead: #constructor.class_method(). Also, you usually say #method() or #property rather than this.method() and this.property in CoffeeScript.