How do I include HTML in a JS Rails response? - javascript

I have a FooController that responds to HTML and JS (AJAX) queries:
# app/controllers/foo_controller.rb:
class FooController < ApplicationController
layout 'foo'
def bar
respond_to do |format|
format.html # foo/bar.html.erb
format.js # foo/bar.js.erb
end
end
end
The templates to support it:
# app/views/layouts/foo.html.erb:
<html>...<%= yield %>...</html>
# app/views/layouts/foo.json.erb:
<%= yield %>
And an AJAX template in which I want to render a partial:
# app/views/foo/bar.js.erb:
dojo.byID('some_div').innerHTML = "<%= escape_javascript(render(:partial => 'some/partial')) %>";
If the JS template just has plain old JS in it (like alert('hi');), it uses my JS template. When I put in the render(:partial), though, it makes the whole response use the HTML template, which means it's no longer valid JS.
A possible solution is to use a function for the layout:
class FooController < ApplicationController
layout :choose_layout
...
private
def choose_layout
return nil if request.xhr?
'foo'
end
end
But my version should work! Why doesn't it?

The most recent Railscast covers this topic (using jQuery).
I'm not quite seeing where you might be going wrong, but here's a snippit from the Railscast that works just fine to render a partial:
// views/reviews/create.js.erb
$("#new_review").before('<div id="flash_notice"><%= escape_javascript(flash.delete(:notice)) %></div>');
$("#reviews_count").html("<%= pluralize(#review.product.reviews.count, 'Review') %>");
$("#reviews").append("<%= escape_javascript(render(:partial => #review)) %>");
$("#new_review")[0].reset();
Where are you storing your Javascript? Do you have an Application.js that you're keeping things in? If so, are you including "dojo" before "application" in your javascript_include_tag?

Try the following;
class FooController < ApplicationController
layout 'foo'
def bar
respond_to do |format|
format.html
format.js { render :layout => false }
end
end
end
Hope that helps.
J.K.

Related

Rails respond_to throw ActionController::UnknownFormat

So, I'm trying to respond to an action with a js file.
def my_schedule
respond_to do |format|
format.js
end
end
In my view, I have 'my_schedule.js.erb' but it's not even executed, rails broke in the controller and throw me an error : ActionController::UnknownFormat, where I have the respond_to.
I tried to add
respond_to :js, :json, :html
at the beginning of my controller out of the actions but still not working.
Need help to debug this and understand how respond_to really works.
format.js will only respond to an xhr request. You can't trigger this response by just navigating to the route that points to this controller and method.
You can test the js.erb execution by changing the respond_to block to
def my_schedule
respond_to do |format|
format.html
format.js
end
end
Then create a my_schedule.html.erb file in the same view folder as the js.erb with the following contents
<%= link_to 'Test', my_schedule_path, data: { remote: true } %>
Note that you may need to adjust that path, I'm just guessing on that.
Then navigate to the same path you were trying to before. You should see a link which, when clicked, will fire the js response.

How to render only js without html (template, layout) in Rails

I have controller Message_Controller and this Controller has method "message" in this method i wanna render .js.erb file i need call js function from rails controller.I need reneder it without html-template(layout) just only js-code in this code i will call js-function with args .How to make it ??
My routes:
post 'chat_bot/message', to: 'chat_bot#message'
My controller:
class ChatBotController < ApplicationController
layout false
def message
#gon.watch.message = params[:text]
#message = params[:text]
puts #message
render partial: 'message.js.erb', layout: false
end
end
my message.js.erb file
alert('<%=#message %>');
With
render partial: 'message.js.erb', layout: false
Rails is going to look for a partial called _message.js.erb right in the folder responding to that controller.
You can use respond_to and there specify the format and what to render:
def message
respond_to do |format|
format.html { render partial: 'message.js.erb' }
end
end
You can skip the instance variable assignation if you prefer, as you have access to the params within the request.
If your idea is to "evaluate" the alert, then it still should be inside a script tag:
<script>
alert("<%= params[:text] %>");
</script>
just update it with
def message
#message = params[:text]
respond_to do |format|
format.js
end
end

Ajax rendering with :remote => true not working on Firefox

My controller actions: advertisment_controller.rb
class AdvertismentsController < ApplicationController
before_action :set_advertisment, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!
skip_before_filter :verify_authenticity_token
layout 'admin'
respond_to :html
# GET /advertisments
# GET /advertisments.json
def index
#advertise_tab = 'active'
if current_user.is_super_admin?
#advertisments = Advertisment.all
elsif current_user.is_admin?
#advertisments = current_user.mediums.last.advertisments
elsif current_user.is_agent?
#advertisments = Advertisment.all.collect{|ad| ad if (ad.discount_rate.present? && ad.discount_rate.discount_provider == current_user.id) }.delete_if{|x| x == nil}
elsif current_user.is_member?
#advertisments = current_user.advertisments
end
end
# GET /advertisments/new
def new
if current_user.is_agent? || current_user.is_member?
#advertise_tab = 'active'
#advertisment = Advertisment.new
load_ad_components
else
redirect_to advertisments_path, flash: {error: 'You cannot create an advertisment'}
end
end
# GET /advertisments/1/edit
def edit
#mediums = Medium.all
#discount_rates = current_user.discount_rates.where(:media_category_id => #advertisment.media_category_id)
#draft_advertisments = current_user.advertisments.where(:draft => true)
respond_to do |format|
format.js
end
end
# POST /advertisments
# POST /advertisments.json
def create
#advertisment = current_user.advertisments.new(advertisment_params)
#advertisment.save
load_ad_components
respond_to do |format|
format.js
end
end
# PATCH/PUT /advertisments/1
# PATCH/PUT /advertisments/1.json
def update
#advertisment.update(advertisment_params)
load_ad_components
respond_to do |format|
format.js
end
end
# DELETE /advertisments/1
# DELETE /advertisments/1.json
def destroy
#advertisment.destroy
flash.now[:success] = "Advertisement deleted successfully."
load_ad_components
respond_to do |format|
format.js
end
end
end
My advertisments/_form.html.erb
<%= form_for(advertisment, :multipart => true, :remote => true) do |f| %>
// form fields
// advertisment is a local parameter
<% end %>
Now, if I submit this form, it is requesting the controller create method as html instead of js.
Also, for edit, I've created the link as
<li>Edit</li>
but when I click on the edit link nothing happens. No ajax call showing in the firebug console. My application.js file contains,
//= require jquery.min.js
//= require jquery_ujs
Whats wrong here? BTW, I've tested the whole edit, create, update, destroy with ajax rendering I've mentioned above in Chrome browser, and its working fine. Does anyone have any idea whats wrong with this code in firefox?
N.B. firebug for firefox show me that both the js files (jquery.min, jquery_ujs) are loaded. I've deleted my browser cache. But still the same. :'(
Any help will be appreciated.

Rails flash notice via ajax

Long story short, I have a button. On clicking it, I want an ajax request to be triggered which gets flash[:notice] and displays it in a div in$
Here is my shortened view:
<input type="button" id="search" value="display"/>
<div id="notice">
</div>
My ajax request in the view:
$("#search").submit(function(){
$.ajax({
type: "POST",
url: //url to my show action
success: function(data){
/*$("#notice").html("<%= flash[:notice] %>");
$("#content").html(data);*/
}
});
return false;
});
My controller:
def HomeController < ActionController::Base
def index
end
def show
respond_to do |format|
format.js { flash[:notice] = "" + count.to_s + " results found for " + params[:query][:search_key] + "" }
end
#render :partial => 'search'
end
end
My show.js.erb
#app/views/dashboard_home/show.js.erb
$("#notice").html("<%=j flash[:notice] %>");
$("#content").html("<%=j render partial: "search" %>");
The problem is when I click on button, the notice is displayed fine. But the same notice persists on the next clicks too. The search partial contains the table Please help!
Here is an example that I got working, thanks to Rich Peck's answer. I needed to use flash.now to make sure the flash notice didn't persist.
AJAX trigger in the view:
<%= link_to "Email report", users_path, remote: true %>
Controller:
# app/controllers/users_controller
class UsersController < ApplicationController
def index
# do some things here
respond_to do |format|
format.js { flash.now[:notice] = "Here is my flash notice" }
end
end
end
Rendered view:
# app/views/users/index.js.erb
$("#flash").html('<%= j render partial: "shared/notice_banner" %>');
where the flash notice is displayed in the layout:
# app/views/layouts/application.html.erb
<div id="flash">
<% if notice.present? %>
<%= render partial: "shared/notice_banner" %>
<% end %>
</div>
# app/views/shared/_notice_banner.html.erb
<div data-alert class="alert-box">
<%= notice %>
×
</div>
Sessions
the same notice persists on the next clicks too
This is caused by the flash being stored in the session variable of Rails:
The flash is a special part of the session which is cleared with each
request. This means that values stored there will only be available in
the next request, which is useful for passing error messages etc.
The problem you have is that since I don't think ajax counts as a new request (need reference for this), the data will persist into the next time you request via HTTP.
--
Fix
I would initially try this:
def show
respond_to do |format|
format.js { flash[:notice] = "my secret number "+rand(0,5)+" !" }
end
end
The main problem you have is you're processing the flash variable in your JS using the ERB preprocessor. This is an issue as it means you won't be able to use asset precompile to help it work.
After looking at this question, why not try using the after_filter callback, like this:
#app/controllers/home_controller.rb
Class Home < ActionController::Base
after_filter { flash.discard if request.xhr? }, only: :show
def show
respond_to do |format|
format.js { flash[:notice] = "my secret number "+rand(0,5)+" !" }
end
end
end
--
Update
You should include the success functionality in your show.js.erb:
#app/views/home/show.js.erb
$("#notice").html("<%= flash[:notice] %>");
This means you can remove the whole ajax call from the application.js, and replace with the remote: true for your search form:
#app/views/search/index.html.erb
<%= form_tag home_show_path, remote: true %>
The reason this works is because when you use the format.js respond block, Rails will load the [action].js.erb file in your views. Considering this only happens after the action has been completed, it's equivalent to the success function of your ajax.
By doing this, you'll be able to remove the entire ajax function from your application.js, and replace with the UJS version, as described above

Easy AJAX HTML element replacement with Rails controllers?

I would love to be able to have an ERB partial like this:
<ul id='things-index'>
<% #things.each do |t| %>
<li><%= t.name %></li>
<% end %>
</ul>
And be able to update it in the controller like so:
class ThingsController < ApplicationController
def create
#thing = Thing.new(params[:thing])
#thing.save
respond_to do |format|
format.html
format.js do
#things = Thing.all
page.replace('things-index')
end
end
end
end
Meaning that JavaScript would be sent as a response without me having to explicity write a js.erb template like the following create.js.erb:
$('#things-index').replaceWith('<%= escape_javascript(render("things/index")) %>')
There may be something like this already, either built in to Rails or available as a gem, but if there is, I'm not aware of it.
I suppose ideally, it would re-render the 'index' action via JS and send the update to the browser, so it might look more like this:
respond_to do |format|
format.html
format.js do
render 'index'
end
end
And know to replace #things-index (or allow me to explicitly specify it).
Update
Whoops...Apparently there was page.replace_html when Prototype was part of Rails, but that functionality has been replaced by the .js.erb template method. That seems much less DRY to me (tons of near-identical js.erb templates), so if anyone has a solution, I'd appreciate it.
You can pass whatever you want to your js.erb, including what to replace, and with what.
$('<%= #id %>').replaceWith('<%= escape_javascript(render(#renderable)) %>')
Note Even in 2012 this wasn't really a recommended way of doing things like this. These days (early 2015) I'd say it's even less recommended given the great client-side technologies available.
Another solution would be to send back just the data, for the client to render as it choses:
respond_to do |format|
format.html
format.json do
#things = Thing.all
render :json => #things
end
end
And on the client side:
function updateList(data){
var $item,
$list = $('#things-index');
$list.find('li').remove();
$.each(data, function(i, item){
$item = $('<li />').text(item.name);
$list.append($item);
});
}
$.getJSON('/my/route.json', function(data){
updateList(data);
});

Categories