4

I'm having some trouble creating functions with CoffeeScript, I guess I've missed something. For my users controller I'd like to create client-side validation for a signup form. I think I've missed something fundamental with how this all works.

<%= form_for @user, :html => {:onsubmit => "return validate_signup_form();"} do |f| %>

CoffeeScript (assets/users.js.coffee):

validate_signup_form = () ->
    alert "Hi"
    return false

Expected output:

var validate_signup_form;
validate_signup_form = function() {
  alert("Hi");
  return false;
};
validate_signup_form();

Real output:

(function() {
  var validate_signup_form;
  validate_signup_form = function() {
    alert("Hi");
    return false;
  };
}).call(this);
Emil Ahlbäck
  • 6,085
  • 8
  • 39
  • 54
  • A better approach to this is binding events to the fields, but if someone has a solution to this particular problem I'd be glad to hear it, it might come in handy some other time. – Emil Ahlbäck Jun 21 '11 at 11:08
  • weird, the online coffeescript almost retrun what you expect. See: http://jashkenas.github.com/coffee-script/ – apneadiving Jun 21 '11 at 12:03
  • Yeah, something changes when Rails includes the file. Could be a feature. :) – Emil Ahlbäck Jun 21 '11 at 12:46

2 Answers2

6

Actually everything works just as it's supposed to. As you can read here, Coffeescript wraps your code in an anonymous function to prevent the pollution of the global namespace. If you just glance at the examples, you might miss this, but the docs clearly state:

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.

In order to access an object, variable or method declared within this artificial scope, you'll need to make it explicitly available within the global scope, e.g. like this:

window.validate_signup_form = validate_signup_form

In the case you're mentioning I'd definitely use events to trigger the method.

Btw.: No need for the empty parenthesis in your method declaration foo =-> works just fine.

polarblau
  • 17,649
  • 7
  • 63
  • 84
  • Thanks for the clarification! Indeed I have only skimmed at the examples. – Emil Ahlbäck Jun 21 '11 at 13:19
  • 2
    "coffeescript --bare" will compile the code without the anonymous wrapper. – Elf Sternberg Jun 21 '11 at 17:31
  • 1
    Right, and [here's how you can enable `--bare` in Rails](http://stackoverflow.com/questions/6099342/how-can-i-use-option-bare-in-rails-3-1-for-coffeescript/6099872#6099872). But don't do this! [Here's why](http://stackoverflow.com/questions/5211638/pattern-for-coffeescript-modules/5212449#5212449). – Trevor Burnham Jun 21 '11 at 19:33
3

polarblau's answer is quite correct. See also:

The closure wrapper is a great way of keeping files modular (as they should be), so I strongly advise against getting rid of it. Note that in the outer scope of your modules, this/@ is going to be window, so you can make your code work as intended by just adding a single character:

@validate_signup_form = ->
  alert "Hi"
  false

It's up to you whether you'd prefer to use @ or window. for defining globals, but the @ style has another advantage: If you reuse your code in a Node.js application, then it'll still work. Those objects will be attached to exports instead of window.

Community
  • 1
  • 1
Trevor Burnham
  • 76,828
  • 33
  • 160
  • 196