Render ERB with underscore templates without displaying in browser - javascript

Am trying to generate a PDF from a HTML file which itself is generated from dynamic content. So there is a controller action "generate_pdf" which takes HTML content and renders a PDF. The pdf generation bit works fine. Generating the HTML is where I am stuck.
#reports_controller.rb
def generate_pdf
#report = Report.where(:id => params[:id])
html_content = render_to_string(:layout => false)
#Use the html_content to generate PDF
end
Now the erb file is as follows :
#views/reports/generate_pdf.html.erb
This is the generated pdf from erb.
The following is a generated content. The report's id is <%= #report.id %>
and the name is "<%= #report.name %>". </br>
It has <%= #report.items %> items.
This erb without any underscore templates works great and render_to_string generates correct HTML content.
But now if I use underscore.js templates ( the same report is also displayed in browser and the application uses Backbone.js and Underscore.js a lot) to generate the content of the report then render_to_string returns incorrect HTML. By incorrect, I mean, the output is the underscore.js template code itself and not the actual content.
{{ _.each(items, function(item) { }}
...
There is no JavaScript environment available inside render_to_string that would render the underscore.js templates. How can I handle this situation? Since most of our templates are in underscore.js, we thought it would make sense to reuse as much as of it as possible instead of creating a different template (in ERB ) just for PDF.

Related

How to render html.erb file to assets/javascript?

Below is the code I have in my assets/javascripts/investment.js file:
investment_updates.forEach(function(investment_update, n){
data.push(new Item((n+1)*space, (n+1)*space, (n)*angle));
add(n+1, "<%= render '/investment_updates/single_investment_update', investment_update: investment_update %>")
});
I want to render views/investment_updates/_single_investment_update.html.erb file in my javascript file and I tried below code but didn't work.
<%= render '/investment_updates/single_investment_update', investment_update: investment_update %>
Help me to render partial in my js file as a parameter.
Your js file should end in .js.erb instead, so it will be picked up by the Rails Asset Pipeline.
Note though that the way you are inserting data in a string is quite brittle. One misplaced ' or " can break your Javascript

Rails - Move view's javascript code to javascript files

First let me tell you that I've searched for this and didn't manage to find any answer for my problem.
I have a lot of Javascript on my layouts/application.html.haml file:
### at the end of application.html.haml file
- if #instance_variable.condition
:javascript
// a lot js/jQuery code
:javascript
// more js code that uses #{ #instance.vars }
- if #another_condition.using.ruby.cody
:javascript
// more js code that uses #{ #instance.vars }
And I'm using instance vars within this code, which means that this vars will only be declared on the controller (application controller). As this is the application layout, these scripts run on every page of my website (and that's what I need of course).
What I want is to move all this code to a js.erb file (or .haml if possible). First - because is easier to manage this code in a separate file; Second - because I don't want to have <script> // a lot of js code </script> tags at the end of every html file, I want to have a <script async src='link'/> tag only.
Also I'm including already the application.coffee at the beginning of the file, but I need this script tag at the end of the html files.
I wouldn't recommend using partials for this. Because, your code uses variables, this means it changes depending on your variables. If you put them into a separate javascript file, the browser wouldn't know about the changes and use a cached file. A workaround would be to add some string (that changes when vars change) at the end of your filename, but then, you would lose all the benefits of moving your javascript into separate files.
A better way would be to define variables in your application.html.haml, move out your javascript code into separate files and just use defined variables.
application.html.haml
- if #instance_variable.condition
%script(src="path/to/my/script.js")
:javascript
= var some_var = #{#instance.vars}
%script(src="path/to/my/second_script_that_uses_var.js")
Thank you for your answer Uzbekjon, but after some research I found a way to do exactly what I wanted :)
In my 'layouts/application.html.haml' file, I added a script tag:
### at the end of application.html.haml file
%script{async: 'async', src: application_scripts_path}
Then I added this path to routes:
get 'application_scripts' => 'controller#application_scripts', as: :application_scripts
Then I just had to set this action application_scriptson my controller and create a new view (app/views/controller/application_scrips.js.erb):
class Controller < ActionController::Base
# 1
protect_from_forgery except: :application_scripts
def application_scripts
# 2
if condition.that.tells.me.this.request.is.valid
# 3
render formats: [:js] and return
end
render plain: ''
end
These steps were of course the harder ones to find out:
1 - I had to disable this protection, or else I would get this error:
ActionController::InvalidCrossOriginRequest at /application_scripts
Security warning: an embedded tag on another site requested protected JavaScript. If you know what you're doing, go ahead and disable forgery protection on this action to permit cross-origin JavaScript embedding.
2 - To make sure that no other site can request this script file (not that it could be a problem for me, but I preferred this way) I added a condition that makes sure the request comes from my website. In this case I was just checking if a user as logged in.
3 - The formats: [:js] tells Rails that the view is not .html, instead it's a .js file 'application_scripts.js.erb'
Finally I just had to move all my code from the application.html.haml file to the view file 'application_scripts.js.erb' and convert from haml code to erb as well.
<% #instance_variable.condition %>
// a lot js/jQuery code
// more js code that uses <%= #instance.vars %>
<% #another_condition.using.ruby.cody %>
// more js code that uses <%= #instance.vars %>
<% end %>
<% end %>

js: undefined local variable or method `current_user'

I've js file with embeded ruby code:
function MyController($scope) {
$scope.sayHello = function() {
var hello = <%= current_user.name %>;
$scope.greeting = hello;
};
}
But I've catch an error undefined local variable or method current_user which points on
= javascript_include_tag "application", "data-turbolinks-track" => true
How can I fix it? Thanks!
Use gon gem https://github.com/gazay/gon the easiest way to pass data from your rails environment to your javascript environement (in other words: from your server to your client).
in your controller
# Make sure first that current_user is not nil
gon.current_user_name = current_user.name
# As you can see we don't pass the whole `current_user` but only the data we need
# gon and javascript won't know how to use your Rails objects
# so pass only strings, integers, arrays and hashs
# (but you'll figure out all of this by your self, it's pretty natural)
in your layout (more info on gon installation can be found in gon github pages)
<head>
<%= include_gon %>
...
in your javascript
var hello = gon.current_user_name;
JS
To extend Benjamin Sinclaire's answer, you have to remember Rails is a back-end system; JS is front-end.
This means all of the Rails variables are only available in the Rails files in the backend - what your browser receives is a pre-rendered set of HTML files, populated with your data
The problem you have is that JS cannot read Rails data without having that data translated from Rails into "JS". All JS sees is the DOM - the HTML elements on your page
--
Data
In order to share variables such as current_user in your JS / front-end, you basically need to pass it through to the HTML layer
You can either do this by setting a hidden element (and setting its data attributes), or by setting the variable in your layout
As mentioned, the most efficient way to do this is to use the gon gem, which Benjamin Sinclaire has discussed

Pass generated HTML to JavaScript

I'm trying to pass a string of generated HTML from Python to Javascript. This is the simplified version of my view (using Pyramid framework).
#view_config(route_name='view_page', renderer='templates/page.jinja2', permission='view')
def view_cosmeceutical(request):
gen_html = markdown( ingredient.desc )
return dict(gen_html=gen_html)
From this thread, I saw that I can use {{ variable }} in Javascript too, so in my JS file, I have a function that attempts to change the innerHTML of the element with the id 'content-desc'. But all I'm getting is the string {{ genHTML }} instead of the actual variable containing the generated HTML.
function insertHTML() {
genHTML = '{{gen_html}}';
$('#content-desc').html(genHTML);
}
What am I doing wrong?
One good way to pass data and content from the server-side Python to JavaScript are
JSON embeds in HTML
Separate AJAX calls which serve JSON objects as application/json mime
For the embed approach, I would do somethibng along the lines to export data to the page template as JSON:
import json
gen_html = ...
javascript_data = json.dumps(dict(gen_html=gen_html))
return dict(javascript_data=javascript_data)
Then in the page template pass this to JavaScript global variables:
<script>
window.javascriptData = {{javascript_data}}; // Check need to escape HTML in your case
</script>
And then in JavaScript (keep preferably in a separate static .JS file):
$(document).ready(function() {
$('#content-desc').html(window.javascriptData.gen_html);
});
Also, I would not generate HTML for just passing it to JavaScript in the first place. Instead, I would pass raw data to JavaScript as JSON, and then generate HTML on the client-side from this data using client-side templating. This increases the complexity, but is more "clean" and flexible.
Example microtemplating engines for the client-side JavaScript:
DOM tree based JavaScript template engines

Ruby json gem is encoding html entities

I make a hash called timeOffsets in my code
#timeOffsets = Hash.new
total=0
#sections.each do |i|
#timeOffsets[i.shortcode] = total
total+=i.length
end
And I render it in Javascript using to_json, :
timeOffsets=<%=#timeOffsets.to_json%>;
but I get it with the HTML entities encoded:
timeOffsets={"Introduction_to_Lists":0,"Removing_elements":693,"Joining__join_":1490};
How do I stop it from encoding the HTML entities?
timeOffsets=<%=raw #timeOffsets.to_json%>
Use the raw view helper.

Categories