Rendering Javascripts in Rails without Asset Pipeline - javascript

My Problem
I am developing a Rails site and would like to use Javascript to render partials in some of my views depending on certain conditions. For instance-- I'd like to have a partial view of a Devise log-in/sign-in prompt come up in a modal box if the user is not signed in when accessing certain pages.
I've had a lot of trouble figuring this out-- the first issue was that I tried using render in the asset pipeline which after some research found doesn't work to begin with.
I then tried putting a js.erb file into my public/javascripts folder but javascript_include_tag force appends '.js' to the file name and using regular src=/javascripts/... didn't render the '.erb' stuff but rather it would append the text <%=j render :partial ... %>
My Solution
I have come up with this solution here and would like to know if there is a better solution to keep clean code. I am going to have a few other Javascripts that will render the same thing over different views.
I have created a app/views/shared/javascripts directory where I will put [filename].html.erb files.
To get the Javascript to run correctly I will <%= render :partial => 'shared/javascripts/...' %> wherever I want that script to be run.
Inside that script is something like:
<script>
$(document).ready(function() {
...
$(.class).append("<%=j render :partial => 'shared/modal' %>");
...
});
</script>
Is There a Better Way?
As far as I can tell-- this will do what I want it to do. I'm just afraid that I'm looking at this all wrong. I'll be working on another part of the app for a while and I really hope to either verify that this is acceptable and decent or find the proper way to ensure that I can use ERB in my JS files.

I know this answer is 3 years late for the OP, but I found this question googling something else and thought I'd answer it as it's something we do quite a lot in our legacy app.
Suppose you want to render the javascript alert("Hello, world!"). You can create the partial _greetings.js.erb in the folder app/views/my_resources:
alert('<%= #msg %>')
And call it from a controller action:
#msg = "Hello, world!"
render partial: 'my_resources/greetings', formats: :js
Hope that helps someone.

One way to do this is to put your partial in your javascript and render it using something like Mustache. The problem with this approach is that if you're rendering a partial you're also rendering traditionally elsewhere in the Rails app, the Mustache template could easily get out of sync with the Rails partial.
Another way to do this would be to have an action which returns the rendered partial as a string, either as XML or wrapped in JSON, and use AJAX to request the rendered partial from that action. Then your Rails controller will handle both rendering the template (using render_to_string) and wrapping it in JSON for your javascript to consume and redisplay.

Related

What is the best approach to manage multi page JavaScript in Rails application?

I am building a Rails application which contains hundreds of html pages and it requires unique javascript on each page including js libraries. Rails because I am more familiar with it and also for other backend purposes.
To get an idea you can take an example of simple code conversion (eg. XML to JSON) online tools. Here the code for converting xml data to json is written in javascript on the HTML page. In my case, I want to make 100's of such tools (ie. csv to json, html to pdf etc), where each one is independent of other. I m not storing any data from these in the database but only the user info who is going to use tools. So basically all pages will be rendered from a single controller.
Please suggest me better approach for that or should I continue writing javascript on every single HTML page. Thanks.
For your case above I would like to suggest using coffeescript OOP method, below is the detail and some code for your requirement
I suggest you to use coffee with class method and then check each page with event turbolinks:load
you can check which page with name of
controller and the the method for example $(".purchase_requests.new")[0] meaning the controller is purchase_requests and the method new
also I suggest you to read brandon hilkert blog for some additional reference below is the link
sample coffeescript with check each page load
class App.PurchaseRequest
renderYourJavascript: ->
console.log "purchase request js"
$(document).on "turbolinks:load", ->
if $(".purchase_requests.new")[0] || $(".purchase_requests.edit")[0]
purchase_request = new App.PurchaseRequest
purchase_request.renderYourJavascript()
I not surely I understand your ideal. But, I think you can try to save your javascript code into database.
And you use a controller for read javascript code from database and render it as javascript file.
Example:
# routes.rb
get '/js/:file_name.js', to: 'javascripts#show'
# model
Javascript( id: :interget, name: :string, content: :text )
# controller
class JavascriptsController < ApplicationController
def show
js = Javascript.find_by_name(params[:file_name])
respond_to do |format|
format.js { js.content if js.present? }
end
end
end
I try to simply describe my ideal. Please checking again and surely it works as you want.
Hope its help.

How do you put ruby inside a javascript function, google maps?

I have a ruby on rails app that has a google map with a marker on it that when you click it it opens an info window using javascript. I want to put ruby var in this javascript fn that displays info from the database in the window.
So when the user clicks the marker they get dynamic info from database not just generic text from js fn.
If anyone has done this before I'd appreciate if you could tell me how you did it , thanks
You can put your javascript directly on your html.erb file using tag and run embedded ruby code within the javascript. Remember to escape_javascript because sometimes your variables will mess up your script by some special characters like apostrophes.
<script>
alert(<%= escape_javascript #variable %>);
</script>
Edward's solution of processing your Javascript files with erb will work (though you will have to remember to add .erb to the end of your JS file so that ERB knows to process it... ie maps.js.erb).
The thing is, this is contrary to the philosophy of the asset pipeline as I understand it. Rails purposely concatenates all your Javascript into one file to save on the overhead of HTTP requests. More importantly, however, browsers cache this file so they do not need to refetch it on every request. If you have a variable that changes the JS file depending on the request made, Rails needs to reprocess the JS file for every request, which is expensive. (Note that the same concept applies to CSS).
The best solution I have found is to put the variable value in the page somehow, since this is one file that needs to be dynamically processed on every request. Usually I see people putting them in hidden forms, as such:
<%= content_tag :input, nil, :type => 'hidden', :id => "map-name-field", :'data-name' => #map.name %>
One approach I have also used is to attach it to an element on the page which is unique to that page, such as a name or title:
<%= content_tag :h2, #map.name, :id => "map-name", :'data-name' => #map.name %>
Once it's on the page, you can fetch from within your JS function (but the key here is that the fetcher function is the same for all isntances of your map, and thus does not need to change or reprocess the JS file):
// Assuming jQuery
// For the hidden input approach:
var mapName = $('#map-name-field').value
// or for the data-attribute approach:
var mapName = $('#map-name').attr('data-name')

rails including javascript or files only on certain pages

Ok Im new to rails in general and these default loaders and cache loaders an all that stuff they make some sense to me. But my question is. If I want to include a certain JS file or 2 or specific script on a particular page how would I do that.
with rails I have
app/views/layouts/application.html.erb in this file I gather is the base template for my site/service, and in there I would put all the moving parts. However I have an occasional need where I only need a javascript file loaded on a particular page. But it needs to be called after the jquery file so jquery is loaded into memory prior to the file I want loaded. So Im not exactly sure how I would approach that. Cause in the layout I have the javascript loader line whatever it is exactly I dont remember but its :default none the less which means jquery and applications will load out by default from what the API tells me.
Which does bring me to another question the guy who initially set up the rails server we have added a file to the defaults I would like to mimic that but don't know how with that either.
The simplest way would be to just include the script file in the view where you need it. jQuery will have already been loaded in the layout.
Alternatively, you can use content_for, as ctcherry mentions. You can find a more detailed explanation here: Javascript Include Tag Best Practice in a Rails Application
Also, regarding you last question, I'm not sure I understand it correctly, but you can add more options to the javascript_include_tag separated by a comma:
javascript_include_tag :defaults, "my_other_file", "etc"
content_for might help you, look at the example that includes the piece of code: <%= yield :script %>
Alternatively, think about ways to allow the JS code to detect if it is begin executed on the correct page (maybe a class or id set on the body tag), and only execute if that condition is met. Then you can serve your entire javascript collection compressed and minified to the user's browser on the first page load, increasing site performance.
May be used for this:
<head>
<title>My blog</title>
<%= yield(:head) -%>
</head>
And send it there from a view:
<%- content_for(:head) do -%>
<%= javascript_include_tag :defaults -%>
<%- end -%>
It's good work!

Where should JavaScript with embedded Ruby code go in a Rails 3.1 app?

For a Rails 3.1 app, some of my site wide JavaScript is only included when certain real time, instance specific conditions are met. This means I can't put it in the new asset pipeline's application.js because that isn't parsed by erb for embedded Ruby within the current context. Basically, I'm including keyboard shortcuts, based on the current_user that is logged in.
My question: where should this JavaScript with embedded code go, so that the embedded Ruby is still parsed for each page access with the proper context (i.e. current, logged in user)?
The answer seems to just be to put it in the application.html.erb layout view at the bottom, but this seams like I'm hiding away javascript code in a non intuitive location.
I've tried creating an application2.js.erb file, but then I got errors about undefined variables, which I think might be because the asset engine only parses this file once before the output is cached and the scope isn't correct yet for things like current_user.
So, using application.html.erb works just fine here, and this isn't so much a question of how to get it to work functionally. Instead, I'm wondering if there's a more elegant way to incorporate the asset pipeline model here with my requirements and still keep most of my JavaScript in the assets/javascripts directory.
You should try to create app/assets/javascripts/application2.js.erb (or whatever better name you come up with)
And then put something like this in your app/assets/javascripts/application.js:
//= require application2
And then you can have
<%= javascript_include_tag 'application2' %>
wherever you want - for example in your application.html.erb.
Btw, if you want to customize what's included on a per-view basis you might find content_for useful. Check out this screencast
Ok, about unobtrusive js. It will be just a cocept (HAML):
In your view somewhere
# hotkeys are "Ctrl+C", "Ctrl+A"
-current_user.hotkeys.each do |hotkey|
%hotkey{ "data-key" => hotkey.key, "data-behavior" => hotkey.fn }
Then in your application.js
$(document).ready(function(){
if($("hotkey").length > 0){
$("hotkey").each{function(this){
key = $(this).data("key");
fn = $(this).data("behavior");
$(document).bind('keydown', key, fn);
}}
}
})
So just the same JS will extract from HTML hotkeys data and then bind it.
As some people have pointed out, the two options are:
Put your javascript inside the view (and as you say, this doesn't feel quite right).
Put it in a javascript file. Make a conditional inside your view that includes this javascript file if certain conditions are met.
If you need to pass more instance variables from the controller to your javascript, this gem called gon can make your life easier.
This allows you to use the default asset pipeline using the following javascript:
if(gon.conditional){
//your embedded js code here
}
If you want to know more about this gem, checkout this railcast where everything gets explained.

What are the Rails best practices for javascript templates in restful/resourceful controllers?

First, 2 common (basic) approaches:
# returning from some FoosController method
respond_to do |format|
# 1. render the out a json representation
format.json { render :json => #foo }
# 2. render an RJS template, say update.js.erb
format.js { render }
end
# in update.js.erb
$('#foo').html("<%= escape_javascript(render(#foo)) %>")
These are obviously simple cases but I wanted to illustrate what I'm talking about. I believe that these are also the cases expected by the default responder in rails 3 (either the action-named default template or calling to_#{format} on the resource.)
The Issues
With 1, you have total flexibility on the view side with no worries about the template, but you have to manipulate the DOM directly via javascript. You lose access to helpers, partials, etc.
With 2, you have partials and helpers at your disposal, but you're tied to the one template (by default at least). All your views that make JS calls to FoosController use the same template, which isn't exactly flexible.
Three Other Approaches (none really satisfactory)
1.) Escape partials/helpers I need into javascript beforehand, then inserting them into the page after, using string replacement to tailor them to the results returned (subbing in name, id, etc).
2.) Put view logic in the templates. For example, looking for a particular DOM element and doing one thing if it exists, another if it does not.
3.) Put logic in the controller to render different templates. For example, in a polymorphic belongs to where update might be called for either comments/foo or posts/foo, rendering commnts/foos/update.js.erb versus posts/foos/update.js.erb.
I've used all of these (and probably others I'm not thinking of). Often in the same app, which leads to confusing code. Are there best practices for this sort of thing? It seems like a common enough use-case that you'd want to call controllers via Ajax actions from different views and expect different things to happen (without having to do tedious things like escaping and string-replacing partials and helpers client side).
Any thoughts?
The best practice is to have your Web UI use the RESTful API. That is, get and put resources in JSON format using JavaScript running in the client, just as a third party might get and put resources in JSON format using RestClient. That means you don't have .rjs or .js.erb templates on the server - you might instead have Mustache or Handlebars or jQuery templates, plus the glue JavaScript, embedded in (or statically linked to from) the HTML page originally delivered to the Web client.
Of course, the quick-and-dirty approach of using remote => true, and going to the server and rendering a JavaScript code template to produce JavaScript that will be executed on the client ... is easier. And it was fairly good practice for a while. But we need JavaScript for a data format now, and we have superior capabilities today for pulling data in JSON format and rendering templates within the client.
Not sure if this work for you, but I would do the following:
Generate .js templates using ERB, these templates will be static
The templates could be mustache, jaml, jQuery Templates or something else.
Use a rake task to generate all templates and create static .js files out of them.
Include these static files using , they will be cached by the browser.
Populate the templates with json data and include the generated html.
This assumes that the templates can in fact be static, which depends on the situation at hand. But generally this should work.

Categories