rails 3 javascript fine coffeescript referenceerror (class) is not defined - javascript

Someone on the RubyRogues podcast once said "Learn CoffeeScript because CoffeeScript writes better javascript than you do." Sorry, can't remember who said it...
So, I took a very simple WORKING javascript function:
togglingii.js
function pdtogglejs(id) { $('div .partybackground').removeClass("hidden"); }
Which is being called by this line:
Read More...
Then I converted it into this coffeescript:
toggling.js.coffee
pdtogglecs(id) ->
jQuery('div .partybackground').removeClass("hidden")
and changed the html to reference the pdtoggle*c*s instead of pdtoggle*j*s.
I can see BOTH of them just fine in my application.js file:
(function() {
pdtogglecs(id)(function() {
return jQuery('div .partybackground').removeClass("hidden");
});
}).call(this);
function pdtogglejs(id) { $('div .partybackground').removeClass("hidden"); }
;
(function() {
}).call(this);
However, only the pure javascript works. The coffeescript always returns Uncaught ReferenceError: pdtogglecs is not defined.
Based on other stackoverflow questions it must be some sort of namespace error. Probably because of the way pdtogglecs is, itself, inside of a function?? However, I have tried defining the coffeescript function with: window.pdtogglecs, this.pdtogglecs, root.pdtogglecs and the coffescript one always fails with that error.
What am I missing??
Thanks!!

You have two problems, one is a bit of CoffeeScript syntax confusion and the other is the namespace problem that you know about.
We'll start by sorting out your syntax confusion. This:
f(x) -> ...
is interpreted like this:
f(x)(-> ...)
So when given this:
pdtogglecs(id) ->
jQuery('div .partybackground').removeClass("hidden")
CoffeeScript thinks you're trying to call pdtogglecs as a function with id as an argument. Then it thinks that pdtogglecs(id) returns a function and you want to call that function with your -> jQuery(...) function as an argument. So it ends up sort of like this:
callback = -> jQuery(...)
returned_function = pdtogglecs(id)
returned_function(callback)
And that's nothing like your original JavaScript. You want to create a function named pdtogglecs which takes id as an argument and then runs your jQuery stuff:
pdtogglecs = (id) ->
# -----^ this is sort of important
jQuery('div .partybackground').removeClass("hidden")
You can see what's going on by looking at the generated JavaScript.
The namespace problem is easy and you can probably figure that out based on the other question you found. However, I'll take care of it right here for completeness.
CoffeeScript wraps each .coffee file in a self-executing function to avoid polluting the global namespace:
(function() {
// JavaScript version of your CoffeeScript goes here...
})();
That wrapper makes everything scoped to the .coffee file. If you want to pollute the global namespace then you have to say so:
window.pdtogglecs = (id) -> ...
You can also say:
#pdtogglecs = (id) -> ...
but I prefer the explicitness of directly referencing window, that also saves you from worrying about what # (AKA this) is when you're code is parsed.

Related

Javascript - pass THIS in function doesnt work

I am very new to Javascript and I just stuck with something that works in python.
The problem is that I have class where I initiate some empty lists as this.data_y_json and etc. If I make normal function inside class like normal_function(){this.data_y_json = 5} it works and the variable is changed.
However, i work with d3, and there is some trick which I cant get through:
// inside class
// in constructor all this.xxx defined
// after object initiation I call set_data()
set_data(){
d3.json("link2.json",function(data) {
for (var i=0;i<data.d.results.length;i++){
this.data_y_json.push(parseFloat(data.d.results[i].PE))
...
//end of function
// end of class
After calling function set_data() an error is raised: SCRIPT5007: Unable to get property 'data_y_json' of undefined or null reference
I am rewriting my visualization into OOP, before this, I had it solved with global variables and it worked fined. In python I would just passed 'self' as an argument to function, but here in javascript, passing THIS doesnt work and raises another error.
Simply said, I know the problem -> this.data_y_json is not recognized propably because of function(data) doesnt pass self of the object, but I dont know how to do it.
Thank in advance for advice
Are you in an ES2015 environment? Changing your callback to be an arrow function should scope this to be what you want
d3.json("link2.json", (data) => {
for (var i=0;i<data.d.results.length;i++){
this.data_y_json.push(parseFloat(data.d.results[i].PE))
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
For reading up on the arrow function and the scoping of this

Jasmine shared specs scoping issues with coffeescript

I'm attempting to DRY up some jasmine tests by extracting out shared examples.
#sharedExamplesForThing = (thing) ->
beforeEach ->
#thingy = new thing
it "is neat", ->
expect(#thingy.neat).toBeTruthy()
describe "widget with shared behavior", ->
sharedExamplesForThing(-> new Widget)
This works nicely when everything is defined in one file. The problems I'm encountering occur when I try to move the sharedExamples to a separate file. I get Can't find variable: sharedExamplesForThing ...
So in the interest of debugging, I tried the following:
describe "widget with shared behavior", ->
it "is acting like a meany", ->
console.log sharedExamplesForThing
expect(false).toBeTruthy()
sharedExamplesForThing(-> new Widget)
In the is acting like a meany block, the log shows sharedExamplesForThing as [Function] but I still get the Can't find variable outside the it. I feel like this might have something to do with a scoping issue outside of my current experience, but I could be completely wrong about that. What am I missing here?
(using rails, jasminerice, guard-jasmine)
I found the piece on shared examples from thoughtbot really useful.
I implemented it in coffeescript as follows:
1) In some helper file that is loaded before all spec files:
window.FooSpec =
sharedExamples: {}
window.itShouldBehaveLike = (->
exampleName = _.first(arguments)
exampleArguments =
_.select(_.rest(arguments), ((arg) => return !_.isFunction(arg)))
innerBlock = _.detect(arguments, ((arg) => return _.isFunction(arg)))
exampleGroup = FooSpec.sharedExamples[exampleName]
if(exampleGroup)
return describe(exampleName, (->
exampleGroup.apply(this, exampleArguments)
if(innerBlock) then innerBlock()
)
)
else
return it("cannot find shared behavior: '" + exampleName + "'", (->
expect(false).toEqual(true)
)
)
)
2) For my specs:
(a) I can define a shared behaviour:
FooSpec.sharedExamples["had a good day"] = (->
it "finds good code examples", ->
expect(this.thoughtbot_spy).toHaveBeenCalled()
)
(b) And use it anywhere in some spec as:
itShouldBehaveLike("had a good day")
(Note: I am assuming the spec has defined this.thoughtbot_spy accordingly before the above line)
When you assign a top-level member variable in CoffeeScript it is assigned as a property of the global object (window in a Browser). So it generates the following JavaScript:
window.sharedExamplesForThing = ...;
That means that you can refer to it outside the file as window.sharedExamplesForThing or just sharedExamplesForThing. So what you are doing should work assuming the shared example file has been loaded before the spec file. I think the problem you have having is that the spec file is loaded first (because describe functions are run as the file is loaded whereas it functions are run after all the files have loaded). So you might need to adjust the load order, you could try putting your shared examples files in a support directory and then requiring this first.
Rather than assigning variables directly to the window object it may be better to set up a namespace to export your shared variables into (so that you don't clutter the global object):
window.MyNamespace = {}
MyNamespace.sharedExamplesForThing = ...
Then in your spec file you can refer to it as MyNamespace.sharedExamplesForThing.
I find it helpful to look at the generated JavaScript to try to understand how CoffeeScript exports variables between files.
Here's the blog post I wrote about how best to do shared examples. Hope this helps.
http://pivotallabs.com/drying-up-jasmine-specs-with-shared-behavior/

ReferenceError: Can't find variable: goToByScroll using CoffeScript

This javascript code is working fine.
function goToByScroll(id){
$('html,body').animate({scrollTop: $("#"+id).offset().top - 50},'slow');
}
I'm having a hard time with its CoffeScript version. I had the following code on application.js.coffee.
goToByScroll = (id) ->
$("html,body").animate ->
scrollTop: $("#" + id).offset().top - 50
, "slow"
But I get the error
ReferenceError: Can't find variable: goToByScroll
Any idea what may be causing the error?
If the function is in a different file it is inside a closure and not accessible. Either attach the function to the window object or declare it in the same file.
What Jordan said is correct: you'll need to somehow export your function
to a globally-accessible scope or compile your CoffeeScript with the bare
flag, preventing the output from being wrapped in an anonymous function.
Additionally, you've got a bit of a bug in your CoffeeScript: you're passing
a callback to jQuery.animate, not an object literal like you do in the
JavaScript code. For equivalent behavior, you probably want something like
this:
goToByScroll = (id) ->
$("html,body").animate
# CoffeeScript supports string interpolation, that's what the #{}
# syntax does
scrollTip: $("##{id}").offset().top - 50
, "slow"
# Then, export it by attaching it to the window or some object accessible
# outside this scope
window.goToByScroll = goToByScroll

How to generate global, named javascript functions in coffeescript, for Google Apps Script

I'd like to write Javascript scripts for Google Apps Script using CoffeeScript, and I'm having trouble generating functions in the expected form.
Google Apps Script expects a script to contain top-level, named functions. (I may be using the wrong terminology, so I'll illustrate what I mean with examples...)
For example, this function is happily recognised by Google Apps Script:
function triggerableFunction() {
// ...
}
... while this function is not (it will parse, but won't you won't be able to trigger it):
var nonTriggerableFunction;
nonTriggerableFunction = function() {
// ...
};
I've found that with CoffeeScript, the closest I'm able to get is the nonTriggerableFunction form above. What's the best approach to generating a named function like triggerableFunction above?
I'm already using the 'bare' option (the -b switch), to compile
without the top-level function safety wrapper.
The one project I've found on the web which combines CoffeeScript and Google App Script is Gmail GTD Bot, which appears to do this using a combination of back-ticks, and by asking the user to manually remove some lines from the resulting code. (See the end of the script, and the 'Installation' section of the README). I'm hoping for a simpler and cleaner solution.
CoffeeScript does not allow you to create anything in the global namespace implicitly; but, you can do this by directly specifying the global namespace.
window.someFunc = (someParam) ->
alert(someParam)
Turns out this can be done using a single line of embedded Javascript for each function.
E.g. this CoffeeScript:
myNonTriggerableFunction = ->
Logger.log("Hello World!")
`function myTriggerableFunction() { myNonTriggerableFunction(); }`
... will produce this JavaScript, when invoking the coffee compiler with the 'bare' option (the -b switch):
var myNonTriggerableFunction;
myNonTriggerableFunction = function() {
return Logger.log("Hello World!");
};
function myTriggerableFunction() { myNonTriggerableFunction(); };
With the example above, Google Apps Script is able to trigger myTriggerableFunction directly.
This should give you a global named function (yes, it's a little hacky, but far less that using backticks):
# wrap in a self invoking function to capture global context
do ->
# use a class to create named function
class #triggerableFunction
# the constructor is invoked at instantiation, this should be the function body
constructor: (arg1, arg2) ->
# whatever
juste use # in script, exemple of my code :
#isArray = (o)->
Array.isArray(o)
it will be compiled in :
(function() {
this.isArray = function(o) {
return Array.isArray(o);
};
}).call(this);
this = window in this case, so it's global function

Unable to re-define a function in my javascript object

I have an object defined using literal notation as follows (example code used). This is in an external script file.
if (RF == null) var RF = {};
RF.Example= {
onDoSomething: function () { alert('Original Definition');} ,
method1 : function(){ RF.Example.onDoSomething(); }
}
In my .aspx page I have the following ..
$(document).ready(function () {
RF.Example.onDoSomething = function(){ alert('New Definition'); };
RF.Example.method1();
});
When the page loads the document.ready is called but the alert('Original Definition'); is only ever shown. Can someone point me in the right direction. I basically want to redefine the onDoSomething function. Thanks, Ben.
Edit
Thanks for the comments, I can see that is working. Would it matter that method1 is actually calling another method that takes the onDoSomething() function as a callback parameter? e.g.
method1 : function(){
RF.Example2.callbackFunction(function() {RF.Example.onDoSomething();});
}
Your code as quoted should work (and does: http://jsbin.com/uguva4), so something other than what's in your question is causing this behavior. For instance, if you're using any kind of JavaScript compiler (like Closure) or minifier or something, the names may be being changed, which case you're adding a new onDoSomething when the old one has been renamed. Alternately, perhaps the alert is being triggered by something else, not what you think is triggering it. Or something else may have grabbed a reference to the old onDoSomething (elsewhere in the external script, perhaps) and be using it directly, like this: http://jsbin.com/uguva4/2.
Thanks for the response .. in the end the answer was unrelated to the code posted. Cheers for verifying I wasn't going bonkers.

Categories