Using rails data collection in .js.erb - ruby-on-rails

I have a books index page which shows all books using the familiair structure:
#books.each do |book|
....
end
On the same page I want to show a Google Chart, using the same #books data.
The chart is triggered by a div:
<div id="chart_div"></div>
And there is a chart.js.erb file:
if ($("#chart_div").length > 0){
google.load('visualization', '1.1', {packages: ['corechart']});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Title', 'Author'],
<%- #books.each do |book| %>
<%= "['#{book.title}','#{book.author}']," %>
<%- end %>
]);
var chart = new google.visualization.AreaChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
}
But this returns the error undefined methodeach' for nil:NilClass, so the#booksis not available in the.js.erb` file. How can I make the books available in the JavaScript? And are there better options to use this data in the chart?

JavaScripts are not compiled in the context of controller action therefore you cannot use instance variables assigned in controller. You have possibly two options:
Load data via AJAX from the server from your chart.js file
Store #book data into your DOM during page rendering eg. as JSON and then read it from the chart.js
Second option is a little bit easier to explain:
Add code something like this to your page with cart:
<script id="chart_data" type="application/json" charset="uff-8>
<%= raw #books.map { |book| [h(book.title), h(book.author)] }.unshift(['Title', 'Author']).to_json %>
</script>
And to your draw_chart function in chart.js load data with
var data_array = JSON.parse($("#chart_data").html());
var data = google.visualization.arrayToDataTable(data_array);
You can also remove erb extension from chart.js you will not need to compile javascripts with erb.
I hope this helps a bit.

Related

Rails 5.2: How do I use page specific JS scripts when using Turbolinks?

I am writing some exploratory javascript for a page. It includes the following line:
dashboard_settings.addEventListener('ajax:complete', function(){
...
I have placed the script just before the closing body tag.
I notice that when I navigate to the page, the JS works as I expect it to and there are no error messages in the console. However, when I navigate away from the page, an error message displays in the JS console:
Uncaught TypeError: Cannot read property 'addEventListener' of null
I understand that this is something to do with turbolinks. However, I am unsure how to go about avoiding it without turning turbolinks off.
Is there any way at all that I can specify that the script loads for the one page only?
I have tried the following without success:
<%= yield :page_scripts %>
<% content_for :page_scripts do %>
<script>
document.addEventListener('turbolinks:load', function(){
...
You can add the listener only if the element exists.
if (dashboard_settings)
dashboard_settings.addEventListener('ajax:complete', function(){...})
Or, if you wan't to add it only for a specific page, you could add a yield statement on your layout and only set it's content con the desired page
#layout
<html>
<body>
...
</body>
<%= yield(:after_body) -%>
</html>
#your view
<%= content_for :after_body do -%>
<script>
document.addEventListener('turbolinks:load', function(){
dashboard_settings.addEventListener('ajax:complete', function(){...})
})
</script>
<%- end -%>

Rails 4 - Moving js inline script inside View to JS file

I would like to move some js inline scripts I have on my homepage to a javascript file (in Assets) but there is some complexity due to variables.
home.html.erb
<div>
this is the homepage
</div>
<script>
<% #deal.deal_details.each_with_index do |popin, index| %>
<% index_plus_one = index + 1 %>
function loadInfoPopin() {
var msg;
msg = Messenger().post({
message: '<%= j render partial: "deals/info_popin/info_popin#{ popin['popin_id'] }",
locals: { popin: popin, index: index_plus_one } %>'
});
}
<% end %>
</script>
For the sake of information here is the format of the Deal's column/attribute 'deal_details' (it's a json attribute):
[{"popin_id":"4","text1":"qqq","text2":"sqsq","image1":"sqqs"},{"popin_id":"5","text1":"sqqs","video1":"s"}]
This is an example and you can have as many json block inside the array as possible.
deals/info_popin/info_popin5.html.erb (it's an example)
<div>
<p>cool image</p>
</div>
</div>
Now, how can I move the whole script or at least the function loadInfoPopin() to a javascript file (that is to say away from the view home.html.erb) ?
Thanks
How about moving the loadInfoPopin() function into a separate JS file and altering the function slightly to take in an argument for the html message?
function loadInfoPopin(html_message) {
Messenger().post({
message: html_message
});
Setup the html for the message within the loop before passing into and calling loadInfoPopin.
.js.erb is an option. This way the js files will be parsed before compiling. Not sure though it will work the way you want.
I would recommend to leave the variables in the layout as vars and moving only the static JS parts to the assets.

In the react-rails gem, what's the difference between using the view helper react_component to render versus using ReactDOM.render?

So vanilla React allows you to use ReactDOM.render, but you have to tie it into the view via a JavaScript reference. Eg, in your JavaScript
ReactDOM.render(
<div>
<MyComponent>Sample Children</MyComponent>
</div>
, document.getElementById('content'));
And in your HTML
<div id="content"></div
But with the helper in the react-rails gem, you only do this in the HTML/view:
<%= react_component('MyComponent') %>
I can only see one downside with the helper, which is that you cannot add children to your component.
Any thoughts?
The react_component helper creates a target node, which eventually receives the component. Here's the relevant part of the gem's UJS:
https://github.com/reactjs/react-rails/blob/master/lib/assets/javascripts/react_ujs_mount.js#L87
A component rendered with <%= react_component %> may have children. Given a parent/child component like this:
var ChildComponent = React.createClass({
render: function() {
return <p>{this.props.number}</p>
}
})
var ParentComponent = React.createClass({
render: function() {
return (
<div>
<ChildComponent number="1" />
<ChildComponent number="2" />
<ChildComponent number="3" />
</div>
)
}
})
These will both render 1 parent + 3 children:
<%= react_component("ParentComponent") %>
or, JS:
React.render(<ParentComponent />, targetNode)
Some advantages to the view helper:
no need to copy, paste and maintain DOM ids for "manually" mounting components
easy to provide props from Rails to React (eg react_component("ParentComponent", { numbers: [1,2,3] }))
Built-in mounting & unmounting for DOM events, Turbolinks or PJAX
Hope that helps!

Rails 4 - how to trigger Object edit form without refreshing the page

Hi and thanks for looking into this.
I have a list of objects displayed on my page. Now when a user selects one of the objects, I'd like to show Edit form for this object without refreshing the page and allow user to update it. I already have a form partial with :remote tag. What I cannot figure out is how to trigger this Edit form and pass the correct object into it.
Thanks in advance!
You can include the form partial for each item in an hidden div, and show the div with javascript only when needed :
<% #items.each do |item| %>
<div>
<%= item.label %>
Edit
<div class="editFormWrapper">
<%= render '_form', locals: {item: item} %>
</div>
</div>
<% end %>
css:
.editFormWrapper {
display: none;
}
javascript (with jquery)
$(document).ready(function() {
$('.editLink').on('click', function() {
$(this).siblings('editFormWrapper').show(); // or .slideDown(); or any other effect
});
});
If the page contains a lot of items, or if the hidden form is big, it may be better to use Ajax to request and include the edit form "on demand", to keep the weight of the page to minimum.
When you render page with objects, you can render objects array in javascript variable
<script>
OBJ_ARR = {
*obj_id1* = {
name: 'test1',
desc: 'test test'
},
*obj_id2* = {
name: 'test2',
desc: 'test2 test'
},
......
}
</scpipt>
Next we use javascript scenario
When object clicked you get certain object_id, and then you get appropriate object from our array
OBJ_ARR[object_id]
So, we put object properties to form and display that form

how to access javascript variable in rails view

Is there any way to access javasript variable inside the rails view.
I just set some value of javascript variable and want to access it in rails view.
Thanks
You have to understand that ruby code in your views gets executed on the server - before any javascript on the page gets a change to be executed.
That's why you cannot do stuff like this:
<script>
var js_var = 1;
</script>
<%= get_value_of_js_var_somehow %>
The other way round it works:
<script>
var js_var = <% generate_value_with_ruby %>;
do_something_in_javascript_with_js_var();
</script>
You can pass a javascript variable to rails using AJAX. For example if you want to pass the id for an user to a method in a rails controller from javascript you can execute the following code:
<script>
var id = 1;
<%= remote_function :url => {:controller=>'controller_name', :action=>'method_name'}, :with => "'user_id=' + id" %>
</script>
You will receive the variable through a POST request, as a parameter. You can access it using params[:user_id].
def method_name
if params[:user_id].exists?
u = User.where('id = ?', params[:user_id]).first
end
puts u.path
end
Hope I've answered your question.
If you have this Javascript variable in a higher piece of javascript (per se: the application.js), then you can always just reference it in the view.
#application.js
var someVar = "Hello World";
Then in your view (executed on the client), you could to...
<script type='text/javascript'>
alert(someVar);
</script>
I think we need more specific ellaboration if one of these three posts doesn't answer your question.
Suppose we are having script as follows
<script type="text/javascript">
var a="some value";
document.getElementById('tagid').innerHTML='<%= tag(:div,content_tag(:p,' " +a+ " '), :id=>' " +a+ " ', name=>"somename") %>';
</script>

Resources