link_to method causes jquery to not load in js.erb - javascript

I am trying to to use a jquery .insertafter method to dynamically insert a row in a table. I have my code in 'create.js.erb' as below:
$("<tr><td><%= #work.id %></td><td><%= #work.user.lname %>, <%=#work.user.fname %></td><td><%= link_to(#work.project.name, #work.project, 'data-no-turbolink' => true) %></td><td><%=#work.datetimeperformed%></td><td><%=#work.hours %></td></tr>").insertAfter("tr#headerrow");
The code works perfectly fine as long as I don't use the 'link_to' method. The moment I use 'link_to', it stops working.
I tired adding an alert before the 'insertafter' method. Adding link_to doesn't even fire the alert.
The page seems to be rendering fine without javascript errors. rails console log looks fine:
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 281110143]]
Project Load (0.1ms) SELECT "projects".* FROM "projects" WHERE "projects"."id" = ? LIMIT 1 [["id", 730774123]]
Rendered works/create.js.erb (3.0ms)
This is my controller code where I am calling create.js.erb. The format.js {} calls create.js.erb.
def create
#work = Work.create(work_params)
respond_to do |format|
if #work.save
format.html {redirect_to #work, notice: 'Work Created'}
format.js { render content_type: 'text/javascript'}
else
format.html {render 'new'}
format.js {}
end
end
end
I tired this solutions from Stack overflow regarding turbolinks and that didn't help either.
Rails, javascript not loading after clicking through link_to helper
Rails 4: disable Turbolinks in a specific page
Using turbolinks in a Rails link_to
Appreciate your help in this.
Thanks!
Mike G

Can I see the version with the link_to in place?
I can't tell from what you've provided but are you sanitizing your html before putting it into the jquery string?
What I think you're doing:
$("<%= link_to 'whatever', 'whatever' %>")
#=> $("whatever")
What you should be doing:
$("<%=j link_to 'whatever' %>")
#=> $("whatever")
The 'j' in the beginning is a method that makes sure it won't escape from a javascript string.
Without more information this is my best guess.

Related

The right way to do remote (ajax) calls in Ruby on Rails

I'm making a Single Page Application with Ruby on Rails (it's my first ruby project ever so I'm definitely missing a lot of stuff yet).
So I have a side menu with some links and the right part of the page is supposed to hold a container which is meant to be filled with some content of partial pages.
The typical menu link I have now looks this way:
<%= link_to t('my-groups'), :controller => 'dashboard', :action => 'mygroups', :remote => true %>
I have a dashboard controller, here's the simplified version of it
class DashboardController < ApplicationController
def mygroups
respond_to do |format|
format.js
end
end
I have a dashboard template with the container div in it
<div class="right_col" role="main">
<h2>This is the default content of center page</h2>
</div>
And here's the routes.rb path for it:
get 'dashboard/mygroups' => 'dashboard#mygroups'
I also have one partial page alogside with my dashboard template and it's called _mygroups.html.erb and a javascript file mygroups.js.erb which is called as my controller action
look at the screenshot of the structure
The contents of this js.erb file are:
$('.right_col').html("<%= escape_javascript(render(:partial => 'mygroups')) %>");
It all works and the partial contents appear inside the container on link click just fine.
But there are still 2 problems I couldn't google the answer for
The questions part:
1) It works with Ajax call but if I simply put this http://localhost:3000/dashboard/mygroups to my browser's navigation line and hit enter, it will give me this error
ActionController::UnknownFormat in DashboardController#mygroups
ActionController::UnknownFormat
Extracted source (around line #70):
def mygroups
respond_to do |format|
format.js end end
How can I avoid this and just redirect to index in this case?
I understand that ajax uses POST, but I tried to use post instead of get in routes.rb for this action, and it didn't work at all
2) What if I have a lot of actions for different partial pages, do I have to create a new js.erb file for each action? Can't it be done in some simplier way with just one file?
3) Is it possible to not specify controller and action on this link explicitly?
<%= link_to t('my-groups'), :controller => 'dashboard', :action => 'mygroups', :remote => true %>
I mean since it's supposed to be a POST ajax request, how come I need to display the url like this http://localhost:3000/dashboard/mygroups to a user?
Add format.html in controller like:
class DashboardController < ApplicationController
def mygroups
if request.xhr?
respond_to do |format|
format.js
end
else
redirect_to root_url
end
end
you can add url in link_to tag like:
<%= link_to t('my-groups'), '/dashboard/mygroups', :remote => true %>
Answers to you questions
When you hit the URL in browser, it sends vanilla HTTP get request(non-ajax) which your controller action is not configured to handle it. You need to add format.html and template named groups.html.erb where generally you will list all the groups, I guess.
respond_to do |format|
format.html
format.js
end
Ideally you have to create separate file for each action but if you can take something common out of different action then you can move common template code to a partial and render it either in a separate template having something special or from the controller action directly.
Yes. The rails way is to use routes helper. Run rake routes to list all available routes in your app and find relevant helpers.
I would strongly suggest to read the rails guide to understand how everything works.

Convert working Ruby on Rails form to AJAX

I have a working Ruby on Rails form that currently posts my results and redirects the user to another view and posts the results. The flow is:
views/tools/index.html.erb -> views/tools/ping.html.erb
Since I have it working now, I'd like to convert it to AJAX and keep the user on the views/tools/index.html.erb view, getting rid of the redirect to enhance the user experience. However, I'm unsure of how to proceed based on the way that my Tools controller is currently setup, and my incredibly lacking knowledge of AJAX.
So, here's what I currently have:
views/tools/index.html.erb (added 'remote: true' to form)
<h1> Tools </h1>
<h3> Ping </h3>
<%= form_tag ping_tool_path(1), method: "post", remote: true do %>
<%= text_field_tag :ip, params[:ip] %>
<%= submit_tag "Ping", name: nil %>
<% end %>
<!-- this is where I'd like to put the results via AJAX -->
<div id="output"></div>
controllers/tools_controller.rb
class ToolsController < ApplicationController
def index
end
def ping
ping_host(params[:ip])
save_host(params[:ip])
# Adds based on recommendations
respond_to do |format|
format.html { redirect_to tools_path }
format.js
end
end
protected
def ping_host(host)
f = IO.popen("ping -c 3 #{host}")
#output = f.readlines
tool_type = "ping"
tool = Tool.find_by(tool_type: tool_type)
tool.increment(:tool_hit_count, by = 1)
tool.save
#results = "<pre>#{#output.join}</pre>".html_safe
end
def save_host(host)
host = Host.find_or_create_by(host_ip: host)
host.increment(:host_hitcount, by = 1)
host.save
end
end
views/tools/ping.html.erb
<%= #results %>
views/tools/ping.js.erb (New file based on suggestion)
$("#output").html("<%= #results %>");
routes.rb
Rails.application.routes.draw do
root 'tools#index'
resources :tools do
member do
post 'ping'
end
end
end
This is what I see on the Network tab in Google Chrome after submitting the form:
So, what I know at this point is that I'll need to add remote: true to my form in views/tools/index.html.erb, and this is where I get lost.
It seems that I have an issue ATM with the fact that I've abstracted the form to use my ping method in the Tools controller, whereas all of the tutorials (and railscasts) I've gone through are doing AJAX on CRUD methods and a given model, not something like what I've build here so far. Please help me understand AJAX!
You're on the right track, now you need to modify the def ping action with a respond_to block.
def ping
ping_host(params[:ip])
save_host(params[:ip])
respond_to do |format|
format.html { redirect_to tools_path } ## if you still want to have html
format.js
end
end
and create a file called view/tools/ping.js.erb where you have javascript that will be run and returned asynchronously
$("#output").html("<%= j #results %>");
The <%= %> block will be evaluated first and replaced with the output of that ruby code. Then this will be inserted into the #output div.

rails 4 js.erb file with block: rest of erb code is ignored

I'm working on a very simple RoR4 forum-like application. This is the forums_controller.rb actions for creating a new forum:
def create
#new_forum = Forum.new(forum_params)
if #new_forum.save
respond_to do |format|
format.html { redirect_to forums_path }
format.js
end
else
respond_to do |format|
format.html { render 'new' }
format.js { render partial: 'form_errors' }
end
end
end
Nothing magic there. The create js.erb looks like this:
<%= add_gritter("This is a notification just for you!") %>
<%= broadcast "/forums/new" do %>
$('#forum_form').remove();
$('#new_forum_link').show();
$('#forums_table').append('<%= j render #new_forum %>')
<% end %>
Everything is actually working fine. The broadcast block call you can see uses Faye to broadcast the javascript managing the changes in the interface.
My concern is, whatever is outside of the block in the js.erb file (i.e. that add_gritter call) is totally ignored. I'm sure it's not a gritter problem because changing that line for a simple alert('hi'); does nothing. However, if the call is moved inside the block it gets executed (with the annoying effect that every client gets the notification, alert or whatever). I am really out of ideas regarding this, and would appreciate any help.
<%= broadcast "/forums/new" do %>
is spitting the result of the execution as javascript (that HTTOK) which makes it go meh. So it should be
<% broadcast "/forums/new" do %>
Note the lack of the =.

How can I render via CoffeeScript in Ruby on Rails 4

I was trying to solve this problem for a while with no result.
I've wanted to pass variables and load a render into a div using CoffeeScript in rails 4.
(I'm using SpreeCommerce platform).
view:
<%= link_to taxonomy.name,root_path+'t/'+tid, {class: "uno", remote: true} %>
controller:
respond_to do |format|
format.html
format.js # menu.js.coffee.erb
end
menu.js.erb.coffee:
$('div#productos').html("<%= escape_javascript(render :partial => /shared/products) %>")
I'd like to load the page '_products.erb.html' and the partial processes the variables that I give it. As soon as I know, view and controller are ok, the problem is in menu.js.erb.coffee
Any help will be apreciated!
ADDITIONAL:
I've modified the extension to .js.coffee.erb. When I try to run the app, it shows me:
"undefined method `render' for #<#:0xa70317c>"
I tryied using <%= raw escape_javascript( render :partial =>... almost always "render" method give me problems.
NEW INFO:
I added gem 'coffee-script' to the Gemfile (then 'bundle install').
Now, when I click the link_to, it shows me into the HTML <%= escape_javascript(render :partial => /shared/products) %> as a text instead of loading the "partial"... any suggestion please?
I wrote a post about this after struggling through the same problem.
You need to:
Name it menu.js.coffee. Suffixing .erb causes it not to be evaluated as CoffeeScript.
Use raw to escape it.
I used these two on my website. Here's how it looks:
<%= raw render 'path/to/menu.js.coffee' %>
It still processes ERB within your CoffeeScript.
I would recommend changing it from menu.js.erb.coffee to menu.js.coffee.erb.
Rails will process the file extensions from right to left. Meaning right now, your file is treated first as coffeescript, then as ruby, and finally as javascript. It looks like you want to make the ruby substitutions first, then parse the coffeescript into javascript, so that would be menu.js.coffee.erb
First of all, you should change file name from menu.js.erb.coffee to menu.js.coffee.erb and you need configuration file as follow, which is a contribution by cervinka on coffee-rails issue #36
config/initializers/coffee_erb_handler.rb
ActionView::Template.register_template_handler 'coffee.erb', Coffee::Rails::TemplateHandler # without this there will be template not found error
class ActionView::PathResolver < ActionView::Resolver
EXTRACT_METHODS = %w{extract_handler_and_format_and_variant extract_handler_and_format} # name for rails 4.1 resp. 4.0
method_name = EXTRACT_METHODS.detect{|m| method_defined?(m) || private_method_defined?(m)}
raise 'unknown extract method name' if method_name.nil?
old_method_name = "old_#{method_name}"
alias_method old_method_name, method_name
define_method(method_name) do |path, default_formats|
self.send(old_method_name, path.gsub(/\.js\.coffee\.erb$/, '.js.coffee'), default_formats)
end
end

jQuery/Rails 3 button does not change state

I have spent a couple of days (like 4) trying to solve this issue. I followed the Hartl Rails 3 tutorial and in chapter 12 tried to convert the site from prototype to jQuery. I am not able to get the "follow"/"unfollow" button to update however.
I am able to issue jQuery commands from within Safari's Inspect Element Console, but if I put even the simplest jQuery command into the destroy.js.erb or create.js.erb files, nothing happens. The log is indicating that the appropriate relationships/destry.js.erb (or create.js.erb) file is rendering.
Here is the code that I have in the controller:
class RelationshipsController < ApplicationController
before_filter :authenticate
def create
#user = User.find(params[:relationship][:followed_id])
current_user.follow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
The users/_follow.html.haml is
- relationship = current_user.relationships.build(:followed_id => #user.id)
= form_for(relationship, :remote => true) do |f|
= f.hidden_field :followed_id
.actions= f.submit "Follow"
The users/_unfollow.html.haml is
- relationship = current_user.relationships.find_by_followed_id(#user)
- delete = { :method => :delete }
= form_for(relationship, :html => delete, :remote => true) do |f|
.actions= f.submit "Unfollow"
The users/_follow_form.html.haml is
- unless current_user?(#user)
#follow_form
- if current_user.following?(#user)
= render 'unfollow'
- else
= render 'follow'
The relationships/create.js.erb is
$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>")
$("#followers").html('<%= "#{#user.followers.count} followers" %>')
The relationships/destroy.js.erb is
$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>")
$("#followers").html('<%= "#{#user.followers.count} followers" %>')
However, in trying to diagnose this, I tried a very basic
$("#title").html("Followed")
which also does not work.
Looks like an issue with how you're using jQuery.
jQuery uses CSS-style selectors, while Prototype doesn't. So, to make it work, just add a "#" symbol to the selectors.
Instead of
$("follow_form").html("something");
You should use
$("#follow_form").html("something");
// Note the # symbol in the selector.
// Also, remember to end your statements with a semicolon.
You can read about jQuery ID selectors here: http://api.jquery.com/id-selector/
Check your public folder for an Assets folder, with application.js in it. Not sure how I ended up with that, but that was causing ajax queries to be processed twice.
It looks like you might be using Rails 3.1. It's important to use the exact gem versions used in the tutorial (including Rails 3.0 instead of 3.1) if want the same results. See the debugging tips at the Rails Tutorial Help page for more suggestions.

Categories

Resources