What Currently Works:
In my BookingsController#Index method, I have this
def index
#user = current_user
if #user.user_type == 'host'
#bookings = Booking.where(host_id:#user.id, status: "#{params[:status]}")
else #user.user_type == 'cleaner'
#bookings = Booking.where(cleaner_id:#user.id, status: "#{params[:status]}")
end
end
This sets the current user and determines which #bookings to display depending on the user_id and params.
In the views, in my index.html.erb file, I render bookings <%= render #bookings %> which i defined in the booking#index. I have a _booking.html.erb partial that gets rendered... Inside the _booking.html.erb partial, I am able to successfully return booking details , like this for instance,
<div id='booking-time'><b>Unit Name:</b> <%= booking.unit.name %></div>
will return the unit name. it recognizes the local variable booking and the fields that belong to it.
I'd Like to Refactor:
I want to refactor this and render a partial <%= render 'bookings/booking' %> in my index.html.erb file. This partial will render different helper methods that each route to different partials.
Currently my _booking.html.erb file has a bunch of conditional logic that is better suited for a helper... I've tried a bunch of different ways to accomplish this to no avail, usually due to undefined variables..what's an elegant way to accomplish this?
What I've Tried:
Here's a method from my bookings_helper.rb` file:
def incoming_to_cleaner_partial_path(bookings)
return unless booking.status=='pending' && current_user.user_type =='cleaner' && booking.requested_by_id != current_user.id
'bookings/booking_requests/incoming_to_cleaner'
end
I added logic to these methods to determine which partials to render.
in my modified _booking.html.erb file, I call these methods, I pass them a #bookings instance variable param.
<%= render incoming_to_cleaner_partial_path(#bookings) %>
I've also tried this without params.. Neither work..
lastly, I've tried calling a method determine_bookings_to_show inside the helper methods. This method contains the same logic from the controller...
def incoming_to_cleaner_partial_path
determine_bookings_to_show
return unless booking.status=='pending' && current_user.user_type =='cleaner' && booking.requested_by_id != current_user.id
'bookings/booking_requests/incoming_to_cleaner'
end
private
def determine_bookings_to_show
#user = current_user
if #user.user_type == 'host'
#bookings = Booking.where(host_id:#user.id, status: "#{params[:status]}")
else #user.user_type == 'cleaner'
#bookings = Booking.where(cleaner_id:#user.id, status: "#{params[:status]}")
end
end
My main issue is i'm having scope troubles with these partials...
.. in my index [I want to] render different partials [depending on attributes of the booking] ..
I have used render(locals:) for this in the past.
# index.html.erb
#bookings.each do |b|
render_booking(b)
end
# bookings_helper.rb
def render_booking(booking)
if booking.status == 'pending'
render partial: 'path/to/bookings/_pending', locals: { booking: booking }
elsif booking.foobar?
render partial: 'path/to/bookings/_foobar', locals: { booking: booking }
else
# etc.
end
end
Conditionals like this can get complicated, but until then, I think this is an appropriate, practical use of a helper method.
# bookings/_pending.html.erb
<h1>Pending Booking <%= booking.id %></h1>
You will likely need to pass other locals:. I'd avoid using any instance variables in your partials.
Related
I am basically trying to use a helper method that renders a partial in my controller and getting a nocontent errors or 500 errors "no template" depending on the variations I'm trying. If I just run the code in the helper directly from the controller action, all is well. It seems like the rails guides don't touch on this (using a helper method that does the rendering, as opposed to directly from the controller action). Unfortunately, I don't have the luxury right now to refactor it out because the helper is used in other places, and it seems to me if there is a way to use this helper in the controller action also, that's the better option.
Anyone know how to do this? Nothing I am trying is working for me :S
Helper method
def render_call_orders_filters url_or_path_method, query_params = #query_params
render :partial => 'call_orders/call_orders_filters', :locals => {url_or_path_method: url_or_path_method, query_params: query_params}
end
Controller action
# GET /call_order/filters
def filters
respond_to do |format|
format.html {
# This (of course) works...
# render :partial => 'call_orders/call_orders_filters', \
# :locals => { url_or_path_method: method(:call_orders_path), query_params: #query_params }
# This does not
helpers.render_call_orders_filters(method(:call_orders_path))
# This gives me a "Template is missing" 500 error
renter text: helpers.render_call_orders_filters(method(:call_orders_path))
}
end
end
based on your last example code, there is render :partial => that works
I think it could be refactored like this
# The helper...
def orders_filters_param_to_template(url_or_path_method, query_params = {})
{
partial: 'call_orders/call_orders_filters', locals: { url_or_path_method: url_or_path_method,
query_params: query_params }
}
end
def render_call_orders_filters(url_or_path_method, query_params = #query_params)
template_data = orders_filters_param_to_template(url_or_path_method, query_params)
render partial: template_data[:partial],
locals: template_data[:locals]
end
# The controller action...
def filters
respond_to do |format|
format.html do
template_data = helpers.orders_filters_param_to_template(method(:call_orders_path), #query_params)
render partial: template_data[:partial],
locals: template_data[:locals]
end
end
end
The error: undefined method `model_name' for nil:NilClass (NoMethodError)
I'm receiving this error when trying to render the haml below:
%section#banner
.row
.medium-12.columns
%h2 Add Testimonial
= simple_form_for(#testimonial) do |f|
.row
.large-6.columns
= f.input :text, as: :text,
placeholder: 'Use this space to write a testimonial about the event(s) you participated.'
.row
.large-6.columns
%p.description
= sanitize('Any testimonial along with your name and profile picture might be used for the promotion of codebar (website, prospectus, etc).')
.row
.large-12.columns.text-right
= f.submit 'Submit testimonial', class: 'button'
The controller is the following:
class TestimonialsController < ApplicationController
before_action :authenticate_member!
def get_testimonial
testimonial = Testimonial.where(member_id: testimonial_member_id)
invitations = current_user.workshop_invitations.accepted_or_attended
if invitations.any? and testimonial.blank?
render 'new'
else
render 'show'
end
end
def show
#testimonial = Testimonial.find(testimonial_member_id)
end
def new
#testimonial = Testimonial.new
end
def create
#testimonial = Testimonial.new(testimonial_params)
#testimonial.member_id = current_user
#testimonial.public = false
if #testimonial.save
redirect_to #testimonial
else
render 'new'
end
end
private
def testimonial_params
params.require(:testimonial).permit(:text)
end
def testimonial_member_id
params[current_user]
end
end
May someone help me see why is returning nil? If the variable is the same I'm passing on the new function?
AFAIK simple_form_for(#testimonial) will try to call #testimonial.model_name so that's where the problem most likely originates.
If you go through the get_testimonial controller, you can end up at:
render 'new'
and that will render the HAML in question. But, notice that nothing in get_testimonial initializes #testimonial so get_testimonial will end up trying to simple_form_for(nil).
Changing the bottom of get_testimonial to something more like this:
if invitations.any? && testimonial.blank?
#testimonial = Testimonial.new
render 'new'
else
render 'show'
end
Your show template presumably needs a #testimonial as well so you might want to say #testimonial = testimonial.first before render 'show' too.
Also, I've changed your and operator to && since you're generally better off pretending that and doesn't exist. The low precedence of and and or cause a lot of problems so you're better off sticking to && and ||.
I'm not sure of the logic for testimonials so you might be able to go with something more like:
def get_testimonial
#testimonial = Testimonial.find_by(member_id: testimonial_member_id)
invitations = current_user.workshop_invitations.accepted_or_attended
if invitations.any? && !#testimonial
#testimonial = Testimonial.new
render 'new'
else
render 'show'
end
end
You might want to revisit your testimonial_member_id method as well, this:
def testimonial_member_id
params[current_user]
end
looks odd, maybe it should be params[:id] instead.
I'm trying to sort out how to loop through two object arrays and display them in mixed order in a view – by the date they were created.
Here's my current controller code, that only displays Posts based on the category you're in, or, based on a search query:
def browse
#user = current_user
if params[:category]
#category = Category.find(params[:category])
#posts = #category.posts.page(params[:page])
else
#posts = Post.search(params)
end
end
In the view, I just loop through and output these like so:
- if #posts
- #posts.each do |post|
= post.name
= post.content
What I'd like to do, is instead of referencing posts via the instance variable #posts... I'd like to create a new variable (ie: #everything) – that pulls objects from the Post class and the Comment class, pops them into the same array, and allows me to loop through and output each respectively in my view, like this:
Ideal controller:
def browse
#user = current_user
if params[:category]
#category = Category.find(params[:category])
#everything = #category.everything(params[:page]) # ... combination of comments and posts
else
#everything = Everything.search(params)
end
end
Ideal view:
- if #everything
- #everything.each do |e|
- if e.type == 'post'
= e.name
= e.content
- else
= e.comment
Any help/guidance is appreciated. I'm just not sure how to approach this.
You would do this type of thing (to get you started)
def browse
#user = current_user
#everything = #category.posts | #category.comments
end
In the view
%ul= render #everything
Make sure there is a views/comments/_comment.html.haml and a views/posts/_post.html.haml files.
Or you could render a specific partial and handle any differences in there
%ul= render :partial => shared/everything_item, :collection => #everthing
in a controller I have a create action
def create
params[:note]
#note = current_user.notes.new(params[:note])
if #note.save
respond_with #note, status: :created
else
respond_with #note.errors, status: :unprocessable_entity
end
end
I want to pass to the model another param called current_user, how to do that and how to retrive the passed param in a model method?
#note = Note.new(params[:note].merge(:user_id => current_user.id))
But perhaps this is not the best way how you do it, look at this: Adding variable to params in rails
If you want access to current_user in model, see: Rails 3 devise, current_user is not accessible in a Model ?
Typically you do that with a hidden_field.
So in your create view, you'd add current_user as a hidden field.
<%= form_for #note do |f| %>
# all your real fields
<%= f.hidden_field :current_user current_user %>
<% end %>
Then in the create controller params[:note][:current_user] would be defined, and added to your model, assuming your model has an attribute called 'current_user'
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.