I have this incredible simple form:
<%= form_tag("/portal/search", method: 'get', remote: true ) do %>
<%= label_tag(:query, 'Search for:') %>
<%= text_field_tag(:query) %>
<%= submit_tag("Find") %>
<% end %>
<div id="results"></div>
I know from the documentation, I can use AJAX through the remote option:
<%= form_tag("/portal/search", method: 'get', remote: true ) do %>
Then I get stuck. I know how to generate results in a view/partial, but how do I get that partial inside the div element? I know a bit of JQuery, so I can select the element. I found a lot of results, but they seem to miss that lightbulb moment for me. I am using Rails 4.1.6
My results are just simple records from a model, like Book (title, author)
Thank you for helping!
EDIT
I've won the cup for missing the point by a long shot. I had to add the search.js.erb inside the view folder instead of assets.
Let's say you get #results out of your search method.
In the controller, search method:
respond_to do |format|
format.html {render or redirect_to wherever you need it}
format.js
end
Then all you need is a file named search.js.erb to run the required js at the end of your request. It might look something like:
$('#results_div').html("<%= j #results.pluck(:title).join('<br/>') %>")
When you add remote: true jquery-ujs will provide you the ajax request (by default this javascript lib is required in app/assets/javascripts/application.js).
The ajax call will request a 'text/javascript' response. for that reason your server code should reply with:
# action
def search_query
respond_to do |format|
format.js {
# additional code
}
end
end
If in your view (search_query.js.erb) you provide javascript, it will be executed. That is why everyone is replying you with a $('#my_div_id').html('my html text') suggestion, which when executed will replace your div content with the new HTML.
If for some reason you want to return a json data structure, then you should provide a different data-type:
form_tag("/jquery_ujs/search_query", remote: true, 'data-type' => :json) do
# ....
end
And you should reply with:
# action
def search_query
respond_to do |format|
format.json { render json: #my_object }
end
end
And handle the success event:
<script>
$(document).ready(function(){
$("form#my_form").on('ajax:success', function(event, data, status, xhr) {
console.log("Great!");
// use data to access to your json data structure
});
$("form#my_form").on('ajax:error', function(event, xhr, status, error) {
console.log("sorry mate!");
});
// ....
})
</script>
You can also request a html response (in case you want to return a table, for instance), with :'data-type' => :html and format.html { render layout: false }
Ajax
Firstly, the Rails UJS (unobtrusive javascript) driver just gives you a "canned" way to send an ajax request to your browser. To avoid confusion, it's best to appreciate that you will be sending Ajax requests regardless of whether you use Rails UJS or the standard Ajax method
This means that the process of capturing the response from your Ajax is still the same - you need to make sure you have to catch the response from the system
Either :-
#app/assets/javascripts/application.js
$(document).on("ajax:success", ".element", function(status, data, xhr) {
// do something here
});
or
#app/controllers/portal_controller.rb
class PortalController < ApplicationController
def search
respond_to do |format|
format.js #-> app/views/portal/search.js.erb
format.html
end
end
end
#app/views/portal/search.js.erb
//something here
Fix
how do I get that partial inside the div element
You'll be able to use JS:
#app/controllers/portal_controller.rb
class PortalController < ApplicationController
def search
#results = ...
respond_to do |format|
format.js
format.html
end
end
end
#app/views/portal/search.js.erb
$(".div").html("<%=j render #results %>");
Related
I need to do the following
I have to post form data using jquery from my view:
form_for(HomeController remote => true) do |f|
Order number: <%=f.text_field_tag :order_number, '1a2b3c4d5e6f7g8h'%>
Amount: <%=f.text_field_tag :amount, 1233.53%>
to my controller where I calculate something:
def calculation
calculations = "expression"
format.json {render json: calculations, status: :created}
respond_to format.json
end
and then post that data (calculations) using redirect to an external link:
$(document).ready(function() {
$('form').submit(function (){
$.ajax({
type: "POST",
url: $(this).attr('data'),
data: $(this).serialize(),
dataType: "JSON"
}).success(function(result){
$.post('link/', $(result).serialize())
})
})
});
However, my browser returns We're sorry, but something went wrong and request results in internal server error. Can anyone see any obvious mistakes?
I believe the mistake is in my view because I have some more code that runs normally when I comment out the part I showed in this query.
Any suggestions?
First: your responder should be:
respond_to do |format|
format.json { render :yourjsontemplate, status: :created, anothervar: :value}
end
Second: In your form_for, add html options like this:
<%= form_for(Object, html: {remote: true}) do %>
Finally, when you do a POST will receive a 'complete'. So, your submit function in JS should be:
.complete(function(result){
$.post('link/', $(result).serialize())
})
EDITED:
form_form use the Model object to create your form, it should be passed as a instance variable initialized in your "new" action inside the controller, like this:
def new
#something = YourModel.new
end
Then, use that variable in form_for:
<%= form_for(#something, html: {remote: true}) do %>
or
<%= form_for(YourModel.new, html: {remote: true}) do %>
Additionally:
Another method to send data from controller to your ajax call is using send_data. So, you can replace the block inside of format.json with:
format.json {send_data calculations} # or whatever you want
I hope that helps you
An update method for one of my controllers looks like this:
def update
#node = Node.find(params[:id])
#video = #node.media
#node.update(node_params)
#video.update(title: #node.name, description: #node.description)
end
I need a callback to make sure that the #node and #video updated. What's the best way to do this?
You can test if the update succeeds...
if #node.update(node_params)
# handle success
if #video.update(title: #node.name, description: #node.description)
# etc...
end
else
# handle fail
end
In an ajax request, you can put the condition in the view:
<% if #node.errors.any? %>
alert('<%= j(#node.errors.full_messages.join("\n")) %>');
<% else %>
alert('Success')
<% end %>
Based on what you said, I think this is what you might want
def update
#node = Node.find(params[:id])
#video = #node.media
#node_saved = true
#video_saved = true
begin
#node.update_attirbutes!(node_params)
rescue ActiveRecord::RecordInvalid
#node_saved = false
end
begin
#video.update_attributes!(title: #node.name, description: #node.description)
rescue ActiveRecord::RecordInvalid
#video_saved = false
end
render "mypage"
end
Inside your update.js.erb
...
if (<%= #node_saved %> === false) {
// trigger flash message
};
if (<%= #video_saved %> === false) {
// trigger flash message
};
I may still be misunderstanding what you want, but this or #Swards answer should answer your question.
Rails' .save and .update return true/false (boolean) depending on whether the action was successful.
The standard way of utilizing that in your flow is as follows:
def update
#node = Node.find params[:id]
respond_to do |format|
if #node.update(node_params)
format.js #-> invokes /views/nodes/update.js.erb
format.json { render json: #node.to_json }
format.html
else
format.js #-> invokes /views/nodes/update.js.erb
format.json
format.html
end
end
end
You shouldn't be using the #video.update using the exact same data as #node - you can do that in the model. You'll be best using associations etc, which I can explain if required.
#app/models/node.rb
class Node < ActiveRecord::Base
belongs_to :media #?
before_save :set_media, on: :update
private
def set_media
self.media.name = self.name
self.media.description = self.description
end
end
Ajax
My front-end UI developer has some jQuery that I need a callback for
Since I don't know the spec for this, I cannot give you any specific code.
What I can tell you is that if you're using ajax, you have to make sure you understand the difference between server-side & client-side code.
I don't want to insult your intelligence but I'll explain it for posterity's sake...
Frontend ajax will look like this:
#app/assets/javascripts/application.js
$(document).on("click", ".element", function(){
$.ajax({
url: "...",
data: ....,
success: function(data) { },
error: function(response) {}
});
});
... since this is client side code, you can only deal with data returned from the server. A common misunderstanding is many people thinking they'll somehow be able to use Ruby code/methods in this. No.
You can only send back pre-packaged data to this, so if you wanted your data to be condition, you'd do something like this:
def update
#node = Node.find params[:id]
#returned = "yes" if #node.update(node_params)
render [x], layout: !request.xhr?
end
This will allow you to send back conditional data, with which you'll be able to use the front-end JS to manipulate:
$.ajax({
...
success: function(data) {
... do something with data
}
--
If you wanted to use server side code, you'll be better using the inbuilt format.js functionality:
def update
#node = Node.find params[:id]
respond_to do |format|
format.js #-> app/views/nodes/update.js.erb
end
end
This does allow you to use Ruby code, as the JS is rendered server-side, and passed to the browser. I'm not sure as to the specifics, but I know you can do the following with it:
#app/views/nodes/update.js.erb
<% if #node.something %>
$("js")....
<% else %>
...
<% end %>
Using rails and .js.erb to make an AJAX request (and append values to a div), how do you prevent rendering a new layout? In other words, stay on the same page without going anywhere and just append the fresh data from the server in a div. No reloading the same page, no redirecting.
At the moment my controller looks like this
def update_shipping
#order = Order.find(params[:id])
#order.shipping_option_id = params[:shipping_options]
#order.save!
respond_to do |format|
format.js
format.html
end
end
and my form like zisss:
<%= form_tag update_shipping_order_path(#order), method: :put, remote: true do %>
<%= select_tag 'shipping_options', #options_for_select, onchange: 'this.form.submit()' %>
<% end %>
and my routes look like a so:
resources :orders do
member do
put :update_shipping
end
end
But I get a 'Template is Missing' error
Please help!!
You need to add a update_shipping.js.erb file under app/views/your_controller/ directory. Note the name of the javascript file should be same as the action. Since you have a remote:true in your form so rails will try to render a javascript template in your case update_shipping.js.erb.
Now in your update_shipping.js.erb file write some basic javascript to update the page elements like
#update_shipping.js.erb
$('.some-div').html(<%=j #model.some_value' %>)
Try this:-
respond_to do |format|
format.js { render :nothing => true }
format.html
end
If you don't want to render a layout, you can use !request.xhr? like so:
respond_to do |format|
format.html { layout: !request.xhr? }
format.js
end
If you're looking to get your ajax-powered JS to fire, you just need to call your .js.erb file the same as your view:
#app/views/controller/update_shipping.js.erb
alert("This JS is returned & fired after the Ajax request");
You'll be best doing this in your routes.rb too:
resources :orders do
put :update_shipping
end
A little late, I came across this searching for the same issue. It must of slipped out of my mind at some point while working with action cable, but what is needed is a http response code of no_content. Http response codes tell the browser how to act when a request is returned. Here is a link to a list of them, and their symbols in rails. More on 204 no content
Here is how it would look:
def update_shipping
#order = Order.find(params[:id])
#order.shipping_option_id = params[:shipping_options]
#order.save!
head :no_content #or head 204
end
edit: what solved the solution for me was a link provided by William Denniss in this stack overflow question
I have already tried reading numerous articles and posts on the subject, but I am still bewildered and can't figure out what I need to do. Can someone explain exactly what I need to do?
I have a Rails server (3.2.11), with a model Game that represents the current state of a Ra game. My games_controller has the methods show for displaying the game and doturn for processing someone's action. The game view shows the current state of the game and contains links for each possible move, which submits back to the doturn action. My goal is to get the view to automatically refresh after a set time period or whenever one of the links is clicked, via AJAX. But try as I might, I can't get either one to work.
My views are currently setup as follows in the /views/games folder.
_game.html.erb actually renders the game
show.html.erb just adds a header and then renders the partial, _game.
show.js.erb is supposed to refresh the page. I'm not really sure how this works
I have also enabled Jquery with the //= require etc lines (they were already there by default)
Edit: I got AJAX on the links working, but I still have no idea how to make it autorefresh after a certain amount of time. Any ideas?
show.html.erb:
<h1>Game <%= #game._id %></h1>
<div id="test"><%= render #game %></div>
show.js.erb:
$('#test').html('<%= escape_javascript (render #game) %>')
Game Controller
# GET /games/1
# GET /games/1.json
def show
#game = Game.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #game }
format.js
end
end
# POST /games
def doturn
error = Proc.new do |message|
flash[:alert] = message
return render :inline => "<%= flash[:alert] %>"
#return redirect_to :back #must return from method immediately
end
#game = Game.where(id: params[:id]).first
if #game.nil?
error.call "Game not found"
end
#all the game update logic here
#game.save
redirect_to :action => "show", :id => #game._id
end
Here's a simplified version of my partial view. I cut out a lot of the rendering stuff, but it still has all the important behaviors. I figure I should try to get this view working first, and then I can add back in the rest of the code later.
<% game.game_players.each_with_index do |player, i| %>
<table>
<tr><td>Player <%= i+1 %></td></tr>
<tr><td>Suns: </td><td><%= player.suns %></td></tr>
<tr><td>Pharaohs: </td><td><%= player.pharaohs %></td></tr>
</table><p>
<%
make_linkt = lambda do |text, kwargs|
concat link_to text, {:remote => true, :method => "post", :action => "doturn", :id => game, :player => i}.merge(kwargs) # ** requires ruby 2
concat raw "<br/>"
end
if i == game.turn
if game.god_status == 0
make_linkt.call("Invoke Ra", :type=>"invoke")
if game.auction_track.size < 8
make_linkt.call("Draw tile", :type=>"draw")
end
else
make_linkt.call("Done", :type=>"god", :vals=>[], :endturn=>1)
end
end
%>
<% end %>
<br />
The project is over, but I figure I should document what I discovered in case anyone else comes across this question in the future. It's been a while, so I don't remember exactly what I did, but here's the best I can recall.
Setting :remote => true in the link_to parameters will mark the link with a special tag that causes it to automatically be turned into an AJAX link by Rails unobtrusive JS. Since it's an ordinary html link until the javascript runs, no extra effort is required to make the page work without Javascript enabled.
In order to get these AJAX links to work, you need two things. First, you have to define a js.erb file. In my case I had this in show.js.erb.
$('#test').html('<%= escape_javascript (render #game) %>')
Note that render #game is a shorthand that will render the partial view _game.html.erb with the #game parameter exposed as game.
Second, you need your controller to respond to the js format. Since my view was being rendered through the show action, I needed to add a respond for show.js. I put the following in my games_controller.
def show
#game = Game.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #game }
format.js
end
end
Getting the autorefresh is more complicated, because Rails doesn't provide magic to do it for you. I ended up creating a recurring function in the javascript to manually refresh the page with AJAX.
I put the following in my view, show.html.erb
<script>
$(document).ready(function() {
setInterval(function() {
$.ajax({
type: 'GET',
url: '<%= url_for(:action => 'show_partial', :id => #game) %>',
data: {},
cache: false,
success: function(result) {
$('#test').html(result)
}
});
}, 2000);
});
</script>
I then added an a show_partial action to the controller so the js could call it.
def show_partial
#game = Game.find(params[:id])
return render #game
end
You could execute a javascript setTimeout method that will be requesting the show.js view.
In your view (or in a .js asset rendered only for your specific page that shows the game):
<script type="text/javascript>
function updateGame(data){
$.ajax({
type: "GET",
url: "/games/whatever-your-path-is.js",
dataType: "script"
});
}
setTimeout(updateGame({gameId: '<%= #game.id %>'}, 5000)
</script>
The data variable in the updateGame, will be available in the ajax call in order for example to "generate" the appropriate url, ex: the line with url could be:
url: "/games/" + data.gameId + "/whatever.js"
I have a CRUD application set up in Ruby on Rails 3 - its working as is. I need to add some ajax here. My first requirement is to retrieve a customised form when clicking on a New Form link. This is the link I have at this point:
<%= link_to 'New Book', new_book_path(:subject_id=>#subject.id), :remote=>true %>
For my controller I've made the following adjustment to the new book action:
def new
#book = Book.new
if params[:subject_id].to_i >0 then
#book.subject_id = params[:subject_id]
end
if request.xhr?
respond_to do |format|
format.html # new.html.erb
format.json { render json: #book }
render :layout => false, :file=>'app/views/books/_form'
return false
end
else
respond_to do |format|
format.html # new.html.erb
format.json { render json: #book }
end
end
end
I checked in firebug and clicking on the link generated returns the form html however I have no idea how to handle the response? Please do help.
Instead of responding with HTML respond with .js
Inside your .js.erb file could be something like this
$("#idname").append(<%= render "form" %>)
That way it renders and returns the form HTML but also gives the js code to append the HTML.
You can certainly rely on Rails generated JavaScript, but I always like to have "more control" over my JavaScript code. In e.g. jQuery you could have something like this (untested), if you want to insert the partial (html) rendered by your controller into the site:
$("#new-book-button").click( function () {
$.get('books/new', function(data) {
$('#some-form-container').html(data);
});
}