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.
Related
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 -%>
In my ruby on rails application, I'm generating a view via an ajax call.
I'm using following piece of code.
$("#a_div_id").html("<%= escape_javascript(render 'index')%>");
And the view I'm trying to render is _index.html.erb:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}
</div>
<script>
alert('first');
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
alert('second');
$scope.firstName = "John";
$scope.lastName = "Doe";
});
alert('third');
</script>
When I render the view, I'm getting only first and third messages. However, when I add this piece of code into dashboard.html.erb rather than rendering it via the ajax code, it perfectly works.
In the first case, I'm getting the following error.
angular.min.js:6 Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.4.8/$injector/modulerr?p0=myApp&p1=Error%3A%2…ogleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.4.8%2Fangular.min.js%3A20%3A274)
at angular.min.js:6
at angular.min.js:38
at n (angular.min.js:7)
at g (angular.min.js:37)
at eb (angular.min.js:41)
at c (angular.min.js:19)
at yc (angular.min.js:20)
at Zd (angular.min.js:19)
at HTMLDocument.<anonymous> (angular.min.js:294)
at fire (jquery.self-bd7ddd3….js?body=1:3233)
I'm be at my wits' end, I couln't decide what I'm missing,
Any suggestions,
Thanks.
Can you try without escape_javascript.
Also put a debugger like this, this will put a break point when chrome executes the js.
debugger;
$("#a_div_id").html("<%= render 'index' %>");
That would help you see what exactly jquery is trying to add as HTML.
If this doesn't work,
Probably you should take the js in script tag and put it in some method and call that method after you have added index to dom.
$("#a_div_id").html("<%= render 'index' %>");
myMethodToRenderAngular();
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!
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.
I will post only the relevant snippet of the file
index.html.erb
<%= render "trace/outbound_message", error => #msg_error,:selector => "#ui-accordion-accordion-panel-3", :tab => "ui-id-1" %>
_outbound_message.html.erb
<% unless error.blank? %>
<%= error.html_safe %>
<script>
$(function() {
show_error(<% selector %>,<% tab %>);
});
</script>
<%end%>
But this function is not able to be called successfully, as I don't think js undersands the embedded ruby code. What is my quickest workaround? Do I have to call a js.erb file, and how do I pass parameters to & in it?
UPDATE
I changed the script to
<script>
alert("check-up");
$(function() {
show_error(<%= selector %>,<%= tab %>);
});
</script>
And even that alert is not being shown? Any ideas?
Use the ruby code in a block like:
In Ruby code:
v = "show_error(<%= selector %>,<%= tab %>);"
In javascript:
<script>
$(function() {
$(#{v});
});
</script>
I think the problem is you quotes ! You should have something like this
show_error(foo,bar);
and you need something like
show_error('foo','bar');
And don't forget to escape your JS :
show_error('<%= escape_javascript(#selector) %>', '<%= escape_javascript(#tab) %>');
How about this ?
<script>
alert("check-up");
$(function() {
show_error(<%= selector.to_json %>,<%= tab.to_json %>);
});
</script>
The to_json function will wrap your variable between quotes, that were missing.
I was playing around, and I figured it out.
<%= javascript_tag( "$(function() {show_error('#{selector}','#{tab}');});")%>
The javascript_tag played a vital role. You can't enclose it in ...
Btw #Saurabh - You were exceptionally close to the answer, no idea why somebody down-voted you.