How to get a Rails variable into JavaScript file (D3) - javascript

I need to grab data from by database and pass it to JavaScript file in my Rails 5 app.
In my controller I create a variable that looks like:
#this_anomaly = Anomaly.find_by_id(2)
I can obviously access it as well as in my view with something like:
<%= #this_anomaly.started_at %>
But I need to get that same data into my javascript file, which I use to draw graphs in D3 to inject into my view. How do I pass it through?
Not sure where to start, thanks!

You could declare your javascript variable in your html.erb like
var started_at = <%= #this_anomaly.started_at %>

One option is to put the data in your HTML in a data attribute
<div data-started-at=<%= #this_anomaly.started_at %> data-for-d3-graphics></div>
Then in your js file
let value = $('div[data-for-d3-graphics]').data('started-at');
I'm not sure if that is the exact syntax for .erb files, but you get the idea

So one method that I tried that works (since I'm using d3) is to request the data in my html.erb with:
<div id="anomaly_data">
<%= #this_anomaly.started_at %>
</div>
Then in my js file, use d3.select to store it in a variable:
var anomaly = d3.select("#anomaly_data").text();
Which works and gets me the date/times/etc without putting them through any processing! But, there must be a better way?

Related

Rails - how put javascript variable into erb code

I've seen some similar question, but still don't know how achieve my goal.
I want to do that thing:
Display modal with products represented by divs.
User choose some products (I add .active class to chosen products)
Then I use jQuery to make array of chosen products ids.
In the end I want to create div for each product, which will be include some informations about this product from database. I'm using js .append() to do it. And here is a problem.
This code is in my script tag in proper view.
var chosen_products_array;
$('.product-to-choose').click(function() {
$(this).toggleClass("active");
$(this).find('i').toggleClass("visible");
});
$('#chosen-products-confirm').click(function() {
var chosen_products = $('.product-to-choose.active');
chosen_products_array = jQuery.makeArray( chosen_products );
});
$('#confirm').click(function() {
var textToInsert = '<div><h4>Products:</h4>';
$.each(chosen_products_array, function(count, item) {
var id = $(item).attr('id').substr(8); // id is product_number
textToInsert += '<div><li name="chosen-product"> <%= current_user.products.find(' + id +').name %></li></div>';
});
textToInsert += '</div>';
$('#div-example').append(textToInsert);
});
My controller create action:
def create
#meal = current_user.meals.build(meal_params)
if #meal.save
current_user.type_tags.each do |type_tag|
key = "type#{type_tag.id}"
if params[key]
#meal.type_tags << type_tag # zapis do bazy danych
end
end
flash[:success] = "Create new meal!"
redirect_to meals_path
else
#render 'meals/new'
end
end
This line generates error:
textToInsert += '<div><li name="chosen-product"> <%= current_user.products.find(' + id +').name %></li></div>';
Error:
Couldn't find Product with 'id'=+id+ [WHERE "products"."user_id" = ?]
I know there is a problem in passing js variables to rails erb, but how can I solve it in this case? I tried with gon too.
In my controller, I added:
gon.products = current_user.products
and then try
alert(gon.products)
it's ok - array of objects, but when I try
alert(gon.products.find(1))
it doesn't work.
Have you got any idea how can I put this javascript id variable to erb code? Or maybe is any different solution?
Probably the best would be to add a remote form (it may be hidden) that posts whatever you want (javascript values) and then it is handled by a controller. Then you can render it with js views.
This would probably be the most "rails-like" solution.
Here's some code:
In the routes:
resource :my_form, only: :create
In the view:
<%= form_tag(my_form_path, remote: true, method: :post) do %>
<%= hidden_field_tag :val %>
<% end %>
You alter the val dynamically by javascript, and submit the form whenever you want to.
Then, in the controller you have something like this:
respond_to :js
def create
# do something with params['val']
#my_val = params['val']
end
And you would also add create.js.erb view, where you could add some javascript that can use any of the instance variables created in the controller.
//create.js.erb
$('#someElement').html('<%= #my_val %>');
Let me stress if it is not clear, the form is remote (ajax), so it is sent in the background and the page is not reloaded.
When the browser receives the response, it may contain any javascript you decide to render (the contents of create.js.erb), and then the browser executes that javascript.
So, you can add there any jquery commands with arguments that you changed dynamically by rails, or you could also render html partials and replace any elements with them, like this:
//create.js.erb
$('#my_id').html('<%= j(render partial: 'my_form/_some_partial.html.erb', locals: { my_val: #my_val }) %>');
More details on Working with JavaScript in Rails
UPDATE
Another, inferior but simpler to implement solution would be to always render all products, but have them hidden initially, and then show them by jQuery on demand. You would just need to add appropriate css classes or data attributes to those products, as well as the links you use to select them.
The problem is that erb code is evaluated on the server before the browser even fetches the javascript file, so it doesn't make sense to use this way. It's recommended that you only use erb in your javascript for simple things like asset_path
I think first make your model visible to the browser as js variable(json object) . Some thing like below at the end of your view file(below is the haml syntax):
:javascript
var userProducts = #{#current_user.products.to_json};
Then use it in javascript expression

Passing a local variable into another view's JS - Rails

Is there a way to grab / fetch a variable inside js between two different views? Say I have an index view with does:
<% foo.each do |foo| %>
<%= link_to 'bar', bar_path %>
<% end %>
Then in bar I want to add some jQuery to a specific element with foo.id selector:
$('div#<%= foo.id %>').fadeOut(); // I know the interpolation wouldn't work here
I know I can pass foo.id to bar with my path or send it via the locals hash, but how would I "grab" it with jQuery?
You can easily pass variables to JS using a gem called gon.
gem "gon"
All you have to do is add this gem to you Gemfile and then you can call gon inside any controller like this:
class PaymentsController < ApplicationController
def new
...
gon.controller = "payments"
gon.action = "new"
gon.message = "Hello World!"
...
end
end
And now, at any javascript (in following case, coffeescript) you can:
$ ->
# Check if it is the target controller and action
if gon? && gon.controller is "payments" && gon.action is "new"
alert gon.message
Is there a way to grab / fetch a variable inside js between two
different views
Not without hacking.
JS is an client-side technology, meaning it loads in the browser, not on the server. This is famous for causing so many issues for developers trying to bind to dynamically-named objects, much like what you're trying to do.
There are several ways around this. I'll go through them for you:
Class
The first way is to use a broader identifier, such as element type or class.
You're currently trying to select foo.id, which means you're going to have to pass that data directly to JS somehow. Inefficient.
A much better way would be to use something like this:
$("a.bar").on("click", function(e){
$(this).fadeOut();
});
--
URL
According to this resource, there are no inbuilt ways to pull query strings from JS: How to get the value from the GET parameters?
You may be able to do something like this:
$(document).ready(function(){
foo = window.location.search.substring(1); //Considering you have http://url.com/your/foo/id
$("#" + foo).fadeOut();
});
--
ERB
There is a final alternative, although this only works if you don't precompile your JS (which I'm not sure is actually feasible any more). That is to use ERB directly in your JS, rather like what you've done in the question.
Whilst this works for dynamic references (such as paths), I don't think it can pull data from a controller. Unless, of course, you put the script directly in your view - which goes against the conventions of MVC.
#app/views/bar/show.html.erb
<script>
// your code here
</script>
I think you need to create a js.erb file and in that pass a variable defined in your controller action.
The code insde js.erb would look like this :
<% fields_html = render "fields", :entity => #model %>
$("#fields-list").html("<%= escape_javascript fields_html %>")
Here fields is a partial I have created. :entity is basically passing of the variable value. #model is the variable defined inside controller action. fields-list is the id of the field I want to load using jquery.

Passing data from javascript into .erb

I want to know if this is possible. I have a bit of javascript code that renders a page based on information from my Rails 2.3.17 application. This code is inherited and not my own. The problem is that I am trying to get data from the server about an object that I do not know about during run time. Basically I am trying to do this:
var record_id = 73;
if(<%= DataTable.find(record_id).value_of_record.nil? %>{
...
}else{
...
where the record_id is what I want to pass into the .erb selection at run time with javascript. Is this possible. And if not, is there any way I can call the information from javascript to get the id of the record passed inside the .erb file? The id is currently being stored in the html document.
As MrYoshiji said, you can use ruby instead so do something like this:
var record_id = <%= record_id = 5 %>;
if(<%= DataTable.find(record_id).value_of_record.nil? %>{
...
}else{
...
Initialising a variable in ruby returns the value so it will become just var record_id = 73; and the ruby variable is now available to the find method.

pass a parameter from a views to a javascript

I have the next variable in my main_controller.rb:
def create:
#token = "24vgd32"
end
in my views/create.erb, I wrote:
<%= javascript_tag do %>
window.my_token = '<%= j #token %>';
<% end %>
by this way, I can use this variable in any of the JavaScript files that this page references.
but how can I use it in my application.js?
this is my application.js:
$(document).ready(function(){
licensario.getLicense({
wizardToken: my_token
});
});
any help appreciated!
In your create.html.erb use this
<%= hidden_field_tag :my_token, #token, :id => 'some_field_id' %>
replace the some_field_id and my_token with desired names.
In your javascript file
$(document).ready(function(){
token = $('#some_field_id').val();
//use your token for any other means.
});
I see 3 ways :
You can use erb in your js file, naming it application.js.erb. However, the data you want to pass must be available when the file is compiled, so this is probably not what you want to do;
You can make a XHR request to your server to fetch the value you want;
You can embed the value in the dom of your view. Some will suggest using a hidden field, I rather tend to use data attributes. For instance, <body data-my-value="32">.... You can retrieve it in your js by doing $('body').data('my-value'). Be careful to call this once the dom is loaded.
I tend to use the latter.
window.my_token makes my_token a variable on the global scope; once it's been defined it is available to application.js as simply my_token.

logic in rails controller vs in javascript (.js.erb)

I have a large "data" active record object whose info I filter based on what the user clicks on a page, then I display that filtered data to the user. I was wondering where the best place to put the data-filtering logic is. Right now I do the filtering in the rails controller action, so that in the view I only have to say something like
<script>
var filtered_data = <%= raw #filtered_data %>
alert('first piece of data is: '+filtered_data[0])
var filtered_names = <%= raw #filtered_names %>
</script>
and other such vars.
or, I was considering just passing the whole #data like:
<script>
var data = <%= data.to_json.html_safe %>
var filtered_data = **some js logic to filter data var**
var filtered_names = **more js logic to filter for names, maybe using the jquery .map function, etc **
</script>
which way is better for performance? I would think client-side JS is better, but it's probably not client side anymore since it's a .js.erb? Or would having more logic in the js.erb still help a bit?
Thanks for any insight!
you should put as few logic as possible into your view...
if you filter the data via js, that data is beeing sent to the client (producing traffic).. so it is probably better to do the filtering on the server side.
always try just to display things in your view - this makes your code much more maintainable.

Categories