include a javascript file only on signup page - Rails & Turbolinks - ruby-on-rails

I need to include a JS file only on the signup page of my rails application. I am using Turbolinks.
I have the below code in application.html.erb just before the <head> section
<%= yield :page_specific%>
and below is the code in my registrations/new.html.erb
<%content_for :page_specific do%>
<%= javascript_include_tag 'page_specific/users/registrations/index', 'data-turbolinks-track' => true%>
<%end%>
This works as expected and loads the script on the registrations page, but if I visit the login page from registrations page, the script remains included in the head section as i have turbolinks enabled.
How can include a page specific javascript only on signup page with turbolinks enabled.

Not sure if its the most elegant way but I managed to fix the issue by disabling turoblinks on login and sign up link by using :'data-turbolinks' => "false"
Hope it helps. Thanks.

Just include javascript_include_tag into view of target page

Related

Rails: How to put stuff in header

In my Rails App I included OneSignal which requires the following link to be placed in the head of the document:
<link href='/manifest.json' rel='manifest'>
Unfortunately with Rails I cannot put this link there, because the entire layout gets rendered inside the body.
While this ...
$(document).ready(function(){
$("head").append("<link href='/manifest.json' rel='manifest'>");
});
seems to work in development mode, it does not work in production.
How can I add this link to my head section of the document?
If you don't want to make a site wide change (adding the script to every page on the site) you can pick the page you want to put it on and add to the .htmlerb file
<%= content_for :header %>
<%= javascript_include_tag "script.js" %>
<%= end %>
javascript_include_tag will allow you to send content from the controller to the header and if script.js is in your app/assets/javascript directory, that will be added to the header.
Another option is to make a specific layout and have the pages that need the script use that layout, by calling it in the controller like so:
def users_index
#user= User.find(params[:id])
layout: users_index layout
end
That will load the users_index_layout.html.erb file from app/views/layouts...if it exists, and complain loudly if it does not.
You can add <link href='/manifest.json' rel='manifest'> in app/views/layouts/application.html.erb.

run coffeescript if user is present

I'm trying to find a way to only run a specific piece of coffeescript code if the user is signed in. Is there a way to achieve this without having to include that specific coffeescript file. For example check if a session cookie with the value of user_id is found, then run this piece of code.
The goal is to remove some inline javascript and move them over to the assets.
Rails by default uses ActionDispatch::Session::CookieStore so theoretically you could read the cookie and get the user id. However to avoid spoofing and session based attacks the cookie is encrypted. And you can't decrypt it without exposing the Rails secret_key_base to the client - which is a really bad idea or forgoing the protection of encrypted cookies.
Instead might just want to let your view / layout tell you if there is a signed in user:
<!DOCTYPE html>
<html>
<head>
<title>Chess</title>
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_include_tag 'application' %>
<%= csrf_meta_tags %>
</head>
<%= content_tag(:body, data: { "signed-in" => user_signed_in?.to_s }) do %>
<%= yield %>
<% end %>
</html>
This will let you read $('body').data('signed_in') and avoids exposing / duplicating any of the actual authentication logic on the client. This also works well with Turbolinks since Turbolinks replaces the body tag.
You would use it like so:
// #return Boolean
function is_user_signed_in(){
$('body').data('signed-in') === 'true';
}
You can move your javascript out to assets.
Expanding on max's answer, it can then look at the value of $('body').data('signed_in')
for example:
if($('body').data('signed_in') == true){
//execute
}
So your javascript still needs to be included in the client's browser but using some logic like that above allows you to only run if there is a user signed in.

Adding custom javascript to device

In my javascripts folder for my rails application, I've added both a device.js file as well as a device/registrations.js file. I was under the impression that when I'm routed to the registration page, rails would automatically pick up the correct javascript files, but for some reason it's not working.
Is there anything that needs to be added so that it can use custom javascript code?
You can include that in your registration view of devise by doing:
<%= javascript_include_tag 'devise/registrations' %>
To generate the views do
rails g devise:views
EDIT
Another way would be in the views/devise folder create a partial _registrations.html.erb
Put your js code in there and then do
<%= render 'registrations' %>

Issues with UJS

I've successfully added ujs in one rails (3.2.6) app. Adding the :remote => true to my form tag allows me to make ajax calls to my js.erb files for dynamic loading of divs, ect..
But in another application on the same machine (Ubuntu 12.0.4), is seems the ujs engine is not working. I'm always getting a Template not Found because the form is sending format => html rather than js. If I force the form to use js format (format => 'js'), it then just renders the js.erb file, rather than calling it via ajax.
In the application.js, I've included the proper headers with the following:
//= require jquery
//= require jquery_ujs
//= require_tree .
The javascript files are included when I actually browse to the primary home page which is using the application layout, which includes the above mentioned javascirpt references. The form in the page is as follows:
<%= form_tag list_path, :remote => true, :id => 'frmBookResults', :method => :post do %>
But although it contains :remote => true, and there's a route established for list_path (the route works, because if I change the list.js.erb to list.html.erb, the view renders), and a method in the controller to handle the request (def list....end), the subsequent list.js.erb is ignored and I get a template not found error, because rails is processing the form request as html, which I can confirm in the log.
I've searched everywhere I could for a solution, but can't figure out why my ujs isn't working for this particular app, when it is nearly identical to my working app, gemset, versions, and configuration.
I've found a couple of other articles on stack overflow where people had the same problem, but no final, working answer was given.
Any help or direction would be greatly appreciated.
It was indeed an issue with the ajax being broken. The onkeyup trigger I was using to submit the form was as follows:
<%= form_tag list_path, :id => 'frmBookResults', :remote => true do %>
<input id='keyword' type='text' onkeyup='document.forms["frmBookResults"].submit();'/>
<% end %>
Note the following:
document.forms["frmBookResults"].submit();
Apparently, submitting the form via javascript was the issue, because when I updated the onkeyup to use a jquery submit as described below, rails ujs kicked in and the ajax calls to my list.js.erb worked:
onkeyup="jQuery('#frmBookResults').submit()"
Thanks for all the feedback mccanff! Your contribution along with other developers from the rails group at linkedIn helped me finally solve my issue.

how should I include a coffeescript file on only one page?

Edit: a year later if I was going to do this again I'd do it with curl.js instead of Rails asset pipeline.
Related: Best way to add page specific javascript in a Rails 3 app?
I'm writing an app and using coffeescript to generate all of the js. That's why the related question doesn't do what I need.
I'd like to be able to put a coffeescript file in a subfolder of my assets directory and have that .coffee file only get served up on one page. The page is on a named route
match 'myNotifications' => 'user#notifications'
The most obvious thing to do was to put the .coffee file in assets\javascripts\user\index.js.coffee. But after reading the docs about assets I'm unclear.
I read this line (from http://guides.rubyonrails.org/asset_pipeline.html):
You should put any JavaScript or CSS unique to a controller inside
their respective asset files, as these files can then be loaded just
for these controllers with lines such as <%= javascript_include_tag
params[:controller] %> or <%= stylesheet_link_tag params[:controller]
%>.
Ok cool, so I put the page specific js in assets\javascripts\user.js.coffee. Then I reloaded my home page, Ctrl F5. The user.js file is still being loaded on the homepage. Tested with $ -> alert 'ready from users controller'; seeing that alert when I load the homepage.
Does Rails have a way to have a per-page coffeescript file that will only be served up with that page? Am I reading the manual wrong? Is there a place in the assets folder that I can put .coffee files where they won't get loaded with every page?
Update: Looks like I might have an answer:
There are a couple of ways that we can get around this problem. We
could use require_directory instead of require_tree as this will only
load the files in the current directory and not in subdirectories. If
we want more control over the included files we can require them
separately instead of including the whole directory. Alternatively we
could move the JavaScript files that we want to be included on all
pages into a public subdirectory. We can then use require_tree
./public to include just those files.
I'll give that a shot in the AM.
Here's the approach I use to make controller/view specific Coffee:
application.html.haml:
%body{ :data => { :controller => params[:controller], :action => params[:action]} }
alt. application.html.erb
<%= content_tag(:body, :data => { :controller => params[:controller], :action => params[:action] }) do %>
...
<% end %>
application.js.coffee:
$(document).ready ->
load_javascript($("body").data('controller'),$("body").data('action'))
load_javascript = (controller,action) ->
$.event.trigger "#{controller}.load"
$.event.trigger "#{action}_#{controller}.load"
users.js.coffee
$(document).bind 'edit_users.load', (e,obj) =>
# fire on edit users controller action
$(document).bind 'show_users.load', (e,obj) =>
# fire on show users controller action
$(document).bind 'users.load', (e,obj) =>
# fire on all users controller actions
Sidenote:
This works great with PJAX as well as you can pass the controller/action names with the response header on PJAX requests and just fire these js functions based on that.
EDIT (2014/03/04):
This solution still works when using turbolinks.js.
Rather than only including the file on one page, you might want to just use logic that's conditional on the page markup. See my answer to a related question. That way, your users don't have to make an additional <script> request for the particular page.
If there's a lot of logic specific to that page (say, 10K+ minified), then yes, split it out. As you suggested in the edit to your question: Rather than doing require_tree . at the root of your javascripts directory, instead create a sub-directory called global and change the top of application.js from
require_tree .
to
require_tree global
Then put your page-specific CoffeeScript file in the root javascripts directory, and point to it with a javascript_include_tag call in that page's template.
include javascript tag into your view template, like show.html.haml
- content_for :javascripts do
= javascript_include_tag 'folder/coffee_file_name'

Resources