29

I have this Javascript view in my Rails 3 project:

app/views/expenses/new_daily.js.erb

var i = parseInt($('#daily').attr('data-num')) + 1;
//$('#daily').append('agrego fila ' + i + ' <br />');

$('#daily').append('<%= escape_javascript(render(partial: 'new_expense', locals: { i: i })) %>');

$('#daily').attr('data-num', i);

I want to pass my 'i' javascript variable to a ruby partial through locals, How I can accomplish this?

fespinozacast
  • 2,484
  • 4
  • 23
  • 38

8 Answers8

23

As far as i know there is no way to do it directly and the reason is fairly simple too, html is executed at the server side and javascript is a client side language which means its executed in your local browser, thats why if you even try to pass a variable between the two you'll have to make a request to the server, However this problem is tackled by calling an AJAX request, this AJAX request does the same thing as sending a new request to the server however it does that without refreshing or reloading the page to it gives the users the illusion that no request was made.

a guy asks a similar question Here

and you can learn more about AJAX Here on MDN:

BigRon
  • 3,182
  • 3
  • 22
  • 47
Umer Hassam
  • 1,332
  • 5
  • 23
  • 42
  • 9
    +1 for the answer, -1 for pointing to w3shcools. Every web developper should be aware that w3schools is very BAD, Take a look at [w3fools](http://w3fools.com/) to find out why. One should use Mozilla Developper Center for documentation instead. – marco-fiset Oct 05 '12 at 12:23
  • 3
    Fyi, HTML isn't executed server-side, ERB is. ERB is the templating language used to generate HTML. The HTML is then sent to the client and rendered client-side. – Ajedi32 Nov 11 '14 at 14:36
16

Yes you can pass the value by using jquery;

<%=f.text_field :email ,:id=>"email_field" %>

<script type="text/javascript">
   var my_email= "my@email.com"
   $(document).ready(function(){
        $("#email_field").val(my_email);
   });
</script>
Thaha kp
  • 3,689
  • 1
  • 26
  • 25
  • 1
    Aha. So now, assuming the form builder f. is modeling :user, hitting the Submit button will send "my@email.com" to the Rails in the params[:user][:email]. Perfect, this is what I was looking for. – Arta Apr 02 '13 at 15:36
  • 10
    The question was how to pass a value from javascript into a Ruby ERB partial rendered in a Rails unobtrusive javascript controller action response (a js.erb file). This answer describes how to set the value of a form field using javascript. So this is not an answer to the question, despite having the most up votes. – user3670743 Sep 12 '15 at 00:30
  • Guys, can you pass also the object.id besides the attributes? I'm using fullcalendar with bootstrap modal in the events controller's index action, and when the window pops up I can set the form attr values of the fields upfront with fullcalendar (so I don't need to find the object upfront with params[:id] in the controller). The reason why I would like to submit it as rails form is to have the server side validation. Could you tell me how to pass the id to the form_for helper to update the given object and how my controller index action would look like with let's say @event object? – Sean Magyar Dec 25 '15 at 19:43
3

Check out the gon gem. https://github.com/gazay/gon

It gives you a simple object you can pass variables to that will be available to your scripts via window.gon

Also referenced here http://railscasts.com/episodes/324-passing-data-to-javascript

Sam Figueroa
  • 2,301
  • 22
  • 21
3

1) You may create a js tag with global variable in you erb template, after that you will be able to access that variable from any js file

<%= javascript_tag do %>
   window.productsURL = '<%= j products_url %>';
<% end %>

2) You can pass data to data-attribute in erb template and access it by js on client side that way $('#products').data('products')

<%= content_tag "div", id: "products", data: {products: Product.limit(10)} do %>
   Loading products...
<% end %>

3) You can use gon, to use your Rails variables in your js

There is a good article, read it and fine solution for your specific case http://railscasts.com/episodes/324-passing-data-to-javascript, more comments are here http://railscasts.com/episodes/324-passing-data-to-javascript?view=asciicast

Julia Usanova
  • 437
  • 5
  • 6
  • Thanks for the pointer to javascript_tag. That and eval within the tag did the trick in a very simple manner. I guess SO users downvote as this is more templating as opposed to 'passing' at runtime. But why use the AJAX gun if you can just write data into the DOM from wihtin you erb when you render the page :) – count0 Jan 26 '18 at 17:34
3

Simple answer is you can't. Partials are expanded at server side, and JavaScript variables are set later at client side. You could make i (as a variable name) a parameter of the partial and use it there.

render :partial => 'xx', :locals => { :variable => 'i' }

And in partial

alert(<%= variable %>);
Marek Sapota
  • 20,103
  • 3
  • 34
  • 47
  • 5
    Why do you state you can't pass a variable and then deliver an example that contradicts your statement, where you essentially pass the contents of `i` to be alerted on the client side through JS? – Sam Figueroa Jan 06 '16 at 06:48
2

The best other answers here are right that this can't be done by passing the javascript variable into an erb partial, since it is rendered on the server, not the client.

But since anyone looking for this is probably interested in a work-around solution, which I don't see here, I will post this example that works well with Rails UJS and Turbolinks.

First, you set up your controller to return a partial as HTML:

format.html { render partial: "new_expense" }

Next, write a javascript AJAX function in app/views/expenses/new_daily.js.erb:

var i = parseInt($('#daily').attr('data-num')) + 1;

$.ajax({
  url: '/daily',
  type: 'GET',
  dataType: 'html',
  contentType: "application/html",
  success: function(response) { 

      $('#daily').replaceWith(response)

      $('#daily').attr('data-num', i);
  }
});

This is going to get your Rails partial as an html fragment that you can use to replace that part of your rendered page. You can use jQuery to get your data-num attribute value, do some math on it, replace the partial in your view, and then set the attribute value again.

You may ask why go to all the trouble of getting the Rails partial and replace it on the page, instead of just getting the data attribute, doing math on it, and setting that? The answer is that this is the best, and perhaps the only way of doing something which is really essential when rendering a Rails partial using UJS while handling an asynchronous response to an action.

If you are handling an asynchronous response from your server in a create.js.erb template, then your variables (@daily, for example) are not going to reflect the work done after the request has completed (for example, if there has been processing on a background server like Sidekiq). In that case you don't have up-to-date action response variables to pass into your Rails partial in the js.erb file, but you also can't pass the javascript data response into your partial, as pointed out in this question.

As far as I know, this approach is the only way to get a fully up-to-date partial after receiving a response to an asynchronous response (not shown). This get you the up-to-date partial, allows you to get your javascript into it, and is flexible enough to work in pretty much any use case.

user3670743
  • 294
  • 3
  • 8
2

Here's a few different options on how to do it:

http://jing.io/t/pass-javascript-variables-to-rails-controller.html

activars
  • 1,664
  • 13
  • 15
Greg
  • 31
  • 1
-1

Let's make shure we understand each other. Your erb template (new_daily.js.erb) will be processed on the server side, ruby code will be evaluated (within <% %>), substitution made, and then resulting javascript will be sent to browser. On the client side the browser will then evaluate this javascript code and variable i will be assigned a value.
Now when do you want to pass this variable and to what partial?

Art Shayderov
  • 5,002
  • 1
  • 26
  • 33