I'm creating a scheduling page that shows a weekly schedule and want to make arrows at the bottom of the page that switch forward and backward a week. To do this I'm creating a variable in the controller, #wbeg, that defaults to the beginning of the week and two actions, week_after and week_before, with respective js.erb files. I can't figure out how to change the #wbeg variable using ajax and need to do so to keep everything on the same page.
Here is the controller:
class HomeController < ApplicationController
def home
end
def scheduling
#work_days = WorkDay.all
#jobs = Job.all
#wbeg = Date.today - Date.today.wday + 1 #beginning of this week
end
def week_before
respond_to do |format|
format.html { redirect_to scheduling_path notice: "You should not see this message, contact admin if you do."}
format.js
end
end
def week_after
respond_to do |format|
format.html { redirect_to scheduling_path notice: "You should not see this message, contact admin if you do."}
format.js
end
end
end
The scheduling page:
<p id="notice"><%= notice %></p>
<h1>Work Day Scheduling</h1>
<table>
<thead>
<tr>
<th>Job #</th>
<th>Job</th>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
<th>Saturday</th>
</tr>
</thead>
<tbody id = "main_table">
<%= render "home/main_table" %>
</tbody>
</table>
<%= link_to '<--', week_before_path, remote: true %>
<%= link_to '-->', week_after_path, remote: true %>
and the two js.erb pages:
$('#main_table').html("<%= j render 'main_table', locals: { wbeg: #wbeg - 7 } %>");
The other:
$('#main_table').html("<%= j render 'main_table', locals: { wbeg: #wbeg + 7 } %>");
I also already tried changing the variable in the week_before and after actions in the controller but it gives the same error 'nil cannot have operation "-"' or something, which just means it's not recognizing the variable.
The way you have it coded now, the value of the #wbeg variable at the time the javascript file is generated will be hard coded into the javascript file. That value will never change. Whatever it was when the javascript was generated is it.
What you need is a javascript variable that you can update in the javascript code which makes the AJAX call.
Related
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 recently started with Rails 3.2 and am stuck while trying to implement some ajax functionality in my app. I followed this railscast completely (http://railscasts.com/episodes/240-search-sort-paginate-with-ajax). Beyond this I want to implement a shortlist button for each product which lets user shortlist products and store them in the session. I also want a small list of shortlisted products to show up on the same page, which needs to be ajax updated.
I am wondering what is the best way to do that. I currently implemented the link_to buttons with remote tag and a helper function to change the link to shortlist/unshortlist. I also, used a conditional div to show the shortlist based on the length of shortlist. However, the issue is that whenever I shortlist, the order of the products table is also reset.
Here are snippets of my code :-
Application_helper.rb
def shortlist_unshortlist_link(product_id )
if (user_session.already_shortlisted? product_id )
link_to 'Unshortlist', { action: 'unshortlist', id: product_id }, remote => 'true'
else
link_to 'Shortlist', { action: 'shortlist', id: product_id }, remote => 'true'
end
end
def shortlist_div
shortlist=user_session.shortlist
if (user_session.shortlist.length > 0)
render :partial => 'pages/shortlist_fields'
end
end
products/index.html.erb
<div id="product">
<% #products.each do |product| %>
<tr>....
<td><%= shortlist_unshortlist_link(product.id.to_s) %></td>
</table>
</div>
<div class="shortlist"><%= shortlist_div() %> </div>
products_controller.rb
def shortlist
user_session.add_to_shortlist(params[:id])
redirect_to products_path
end
def unshortlist
user_session.remove_from_shortlist(params[:id])
redirect_to products_path
end
I think, the issue is because of redirect_to, but I am not getting how to avoid this without hurting the functionality. Am I on a totally wrong path here. Is this a good way to implement this. Any suggestions.
thanks,
Amit
you should use in shortlist method,
respond_to do |format|
format.html {redirect_to products_path }#shortlist.html.erb
format.js #shortlist.js.erb
end
and write your java script to #shortlist.js.erb file.
Do the same with unshortlist.
I agree with Sush, you didn't response to the ajax request from link_to.
In your controller, shortlist method, response to the ajax request
respond_to do |format|
format.html {redirect_to products_path }
format.js
end
By convention in Rails, format.js will execute the js.erb file with the same name as your method.
And in the shortlist.js.erb, you may write something like:
$('#shortlist').html('<%= escape_javascript(render "pages/shortlist_fields")%>');
Besides, you can also call the same js.erb file.
In the unshortlist method, you can do it like that:
respond_to do |format|
format.html {redirect_to products_path }
format.js {render :action=>'shortlist'}
end
I'm wanting to add some AJAX functionality to my Rails app, but have no idea where to start.
Here is the method that adds an item to an order:
def add_item_to_order
if session[:order_id].nil?
#order = Order.new #Don't create an order until there is an item to be added to it.
#order.account_id = session[:user_id]
else
#order = Order.find(session[:order_id])
end
item = Item.find(params[:id])
o_item = OrderItem.new
o_item.item_id = item.id
#order.order_items << o_item
#order.total += item.sale_price
#order.save
session[:order_id] = #order.id
redirect_to order_home_path
end
This is run when the user clicks:
<%= link_to item.name, add_item_to_order_path(item.id), :class => "fixed medium green button"%>
Can anyone give me any tips on how to get started, so the the item is added to the order via AJAX?
Check on how to render javascript. In normal requests one would redirect to some action or render some view etc, for a XHR (XmlHttpRequest) you can render javascript through a server-sided js template that would be rendered. You will have to use the LegacyPrototypeHelpers provided for Rails-3 as the original helpers were only officially available for Rails-2.
A better approach(unobtrusive as Rails 3 prefers) will be to just send some data from the server. In the following example you have above I guess if you send item.id via a JSON object or some other format and then read it in the success callback of the place from where you made the XMLHttpRequest, then after getting the item.id you could create the HTML that the link_to creates and then append it to the DOM.
Great tutorial, did this myself: http://ruby.railstutorial.org/ruby-on-rails-tutorial-book Chapter 12 has some stuff on Ajax.
Important part is to set your link_to paramater data-remote to true:
<%= link_to item.name, add_item_to_order_path(item.id),
:class => "fixed medium green button" data-remote="true" method="post"%>
and in your controller you add
def add_item_to_order
# other stuff
# at the bottom:
respond_to do |format|
format.html { redirect_to order_home_path }
format.js
end
end
Then you'll need a .js.erb file to handle the format.js repsonse:
$("your_form").update("<%= escape_javascript(render('partial_page')) %>")
and a partial page file to hold the new data..
I'm trying to create a helper function that strips all stopwords from the content field of data. I've used the basic scaffolding, like so:
rails generate scaffold MyData pageId:integer content:text
I've added a private method in the controller as such:
private
STOP_WORDS = %w{a am an as at be by do go he i if in is it me my no of on or so to un up us we}
def remove_stop_words(lowercase_string)
lowercase_string.gsub(/\b(#{STOP_WORDS.join('|')})\b/mi, '')
end
and now I'm wondering, since the controller is
def index
#tube_data = TubeDatum.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #tube_data }
end
end
and the corresponding view is
<% #tube_data.each do |tube_datum| %>
<tr>
<td><%= tube_datum.pageId %></td>
<td><%= tube_datum.content %></td>
....
how to go about making each tube_data.content stripped?
Thanks!
Add the function in: app/helpers/application_helper.rb
STOP_WORDS = %w{a am an as at be by do go he i if in is it me my no of on or so to un up us we}
def remove_stop_words(lowercase_string)
lowercase_string.gsub(/\b(#{STOP_WORDS.join('|')})\b/mi, '')
end
In the view:
<%= remove_stop_words(tube_datum.content) %>
Move that code from your Controller into application_helper.rb in the app/helpers folder and wrap it in a method called stripstopwords.
Then in your view go stripstopwords tube_datum.content
I'm trying out the beast forum written in rails and will use this as an example of a problem I keep facing.
The forum has a topics/show action and view with a form at the bottom to create a new post within the topic.
Submitting the form goes to posts/create and if the validation passes redirects back to topics/show and works fine, however if the validation fails (leaving out the body field) you're redirected to the same topics/show and back to the form, with no validation errors... normally if validation fails you're left on whatever/create with render :action => new.
Are the validations being lost in the redirect, and what's the best method of getting it working?
See code below:
PostsController.rb
def create
#post = current_user.reply #topic, params[:post][:body]
respond_to do |format|
if #post.new_record?
format.html { redirect_to forum_topic_path(#forum, #topic) }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
else
flash[:notice] = 'Post was successfully created.'
format.html { redirect_to(forum_topic_post_path(#forum, #topic, #post, :anchor => dom_id(#post))) }
format.xml { render :xml => #post, :status => :created, :location => forum_topic_post_url(#forum, #topic, #post) }
end
end
end
TopicsController.rb
def show
respond_to do |format|
format.html do
if logged_in?
current_user.seen!
(session[:topics] ||= {})[#topic.id] = Time.now.utc
end
#topic.hit! unless logged_in? && #topic.user_id == current_user.id
#posts = #topic.posts.paginate :page => current_page
#post = Post.new
end
format.xml { render :xml => #topic }
end
end
topics/show view
<% form_for :post, :url => forum_topic_posts_path(#forum, #topic, :page => #topic.last_page) do |f| %>
<%= f.error_messages %>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td rowspan="2" width="70%">
<%= f.text_area :body, :rows => 8 %>
</td>
<td valign="top">
<%= render :partial => "posts/formatting" %>
</td>
</tr>
<tr>
<td valign="bottom" style="padding-bottom:15px;">
<%= submit_tag I18n.t('txt.views_topics.save_reply', :default => 'Save reply') %>
</td>
</tr>
</table>
<% end %>
Many thanks.
I think you have two problems here.
Keeping validation errors through a redirect
Repopulating the form fields so the user doesn't have to enter all the information again.
Both of these things are connected.
Validation errors are usually displayed through the error_msg_for method which acts on an object. Usually provided by the controller as the an instance variable of object that could not be saved. That same instance variable is used to repopulate the form.
During a redirect the controller will usually instantiate an instance variable using the params hash. So any information used to determine why a save failed is lost. Normal resources will render on save failure and redirect on success, this causes two things happen.
The instance of the object is passed to error_msg_for creating that nice uniform error box.
The instance of the object is used to populate the fields of the form, allowing your user to edit only what is necessary.
I don't know Beast so well, so I'm not sure if the form to create threads is an active record model. But the following will give you an idea how to work around your problem. It would involve modifying your local copy of the Beast plugin, so if you're using a tool to keep it updated, your changes might get lost.
I've been using these following methods to get your validation problems. Assuming the form you're talking about is based on a nmodel they should provide you with everything you need to repopulate a form.
Essentially you store a shallow copy of the object with the errors in the flash hash, using clone_with_errors. You have to use a shallow copy or else you'll run into problems when displaying errors for records with multiple associations.
Then I use my_error_msg_for which takes the same options as the standard error_msg_for to build the error messages html. I only wrote it because for some reason the standard error_msg_for method didn't work with objects stored in the hash. It's almost identical to the official source version of error_msg_for which was troubling.
/app/controllers/examples_controller.rb
class ExamplesController < ApplicationController
def update
...
if #example.save
regular action
else
flash[:errors] = clone_with_errors(#example)
respond_to do |format|
format.html redirect_to(#example)
end
end
end
/app/views/examples/show.html.erb
<div id="error">
<% if flash[:errors] && !flash[:errors].empty? then -%>
<p ><%= my_error_msg_for flash[:errors] %></p>
<% end -%>
</div>
...
Here's the code you need to make it all work.
application_controller.rb
def clone_with_errors(object)
clone = object.clone
object.errors.each{|field,msg| clone.errors.add_to_base(msg)}
return clone
end
application_helper.rb
def _error_msg(*params)
options = params.extract_options!.symbolize_keys
if object = options.delete(:object)
objects = [object].flatten
else
objects = params.collect {|object_name| instance_variable_get("##{object_name}") }.compact
end
count = objects.inject(0) {|sum, this| sum + this.errors.count }
unless count.zero?
html = {}
[:id, :class].each do |key|
if options.include?(key)
value = options[key]
html[key] = value unless value.blank?
else
html[key] = 'errorExplanation'
end
end
options[:object_name] ||= params.first
options[:header_message] = "#{pluralize(count, 'error')} prohibited this #{options[:object_name].to_s.gsub('_', ' ')} from being saved" unless options.include?(:header_message) && !options[:header_messag].nil?
options[:message] ||= 'There were problems with the following fields:' unless options.include?(:message) && !options[:message].nil?
error_messages = objects.sum {|this| this.errors.full_messages.map {|msg| content_tag(:li, msg) } }.join
contents = ''
contents << content_tag(options[:header_tag] || :h2, options[:header_message]) unless options[:header_message].blank?
contents << content_tag(:p, options[:message]) unless options[:message].blank?
contents << content_tag(:ul, error_messages)
content_tag(:div, contents, html)
else
''
end
end
def my_error_msg_for(params)
_error_msg_test :object_name => params[:object].class.name.gsub(/([a-z])([A-Z])/,'\1 \2').gsub(/_/, " "),
:object => params[:object], :header_message => params[:header_message], :message => params[:message]
end
I'm afraid I don't know anything about Beast, but speaking generically, everything is lost when you redirect. It's a new page request, and everything is reset unless it's been stored somewhere (the database or the session, normally.)
The normal flow you tend to see with forms is to redirect if the object is saved, but to render if the save fails. The view file can then pick up whatever variables have been set in the controller - which would normally include the object that didn't save and its validation messages.
Sorry that doesn't solve your problem, but hopefully it may give you some clues.
My answer to a very similar question posted more recently here on StackOverflow covers a number of pros and cons to the redirect_to vs. render debate. I'd love to hear if anyone has any other pros/cons to add to the discussion.