I have looked around S.O. and other resources on the web and I am finding other questions that are similar to this one but not exactly like mine. I'm sure that there is a response out there that works but I am asking this question for clarification as much as I am asking to find the solution. Sorry if this sounds redundant to other questions but I have yet to find something that definitively answers my question.
I have an app where businesses have a listing page and each of those pages has a section where users can submit comments about that business.
Each business page is handled via a 'merchants_controller' using the 'show' action:
def show
#merchant = Merchant.merchant_link_test(params[:id])
#title = #merchant.name
# used inside maps javascript
#map_height = '290px'
#map_width = '350px'
#user_reviews = Review.where(:merchant_id => #merchant.id).order("created_at....
#user_reviews_count = Review.where(:merchant_id => #merchant.id).count
#user_or_merchant = 'merchant'
#review = Review.new
#average_rating = Review.where(:merchant_id => #merchant.id).average(:rating)
#rounded = round_rating(#average_rating)
#yelp_query = #merchant.yelp_string
#yelp_count = yelp(#yelp_query, 'avg_rating')
#num_of_yelp = yelp(#yelp_query, 'review_count')
end
I have a partial in the 'show' layout that displays a form for submitting comments about that business. The form input is handled via a 'reviews_controller' using the 'create' action:
def create
user = User.find_by_id(current_user.id)
id = user.id
#merchant = Merchant.find(params[:review][:merchant_id])
params[:review].store(:user_id, id)
#review = Review.new(params[:review])
if #review.save
flash[:success] = "Your review was submitted!"
redirect_to #merchant
else
flash[:error] = "There was an error submitting your review"
render :template => 'merchants/show', :locals => { :id => #merchant.id, }
end
end
The form uses the 'form_for' helper and looks like this:
<% if signed_in? %>
<div class = "comment_form">
<%= form_for #review do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<div class = "field">
<div id = "required_rating"> * </div><%= f.label :rating %>
</br>
<%= f.select :rating, [['', ''],['1 star (terrible)', 1],
['2 stars', 2],['3 stars', 3],['4 stars', 4],['5 stars
(bomb.com)', 5]] %>
</br>
</div>
<div class = "field">
<div id = "required_comment"> * </div><%= f.label :comment,
"Comment (limit 1000 characters)" %>
<br/>
<%= f.text_area :comment, :cols => 80, :rows => 6 %>
</div>
<div id = "denotes_text"> * Denotes required field </div>
<div class = "actions">
<%= f.submit "Submit Review" %>
<% end %>
<% else %>
<div class = "sign_in_to_comment">
<%= link_to "Sign in to submit a review of this merchant",
signin_path %>
</div>
</div>
<% end %>
I have a two tables and two models, 'merchants' and 'reviews' that handle the data for the respective resources. My 'routes' file looks like this:
resources :merchants, :only => [ :new, :create, :show ]
match '/merchants/:name', :to => 'merchants#show'
match '/merchants/:id/all', :to => 'comments#all_comments_merchant', :as => :all
match '/merchants/:id/map', :to => 'merchants#map', :as => :map
resources :reviews, :only => [ :create ]
My issue is that when a user submits a comment, if an error is generated via my validations in the model, the error object is passed back to the create action in the 'reviews_controller.' I then want to display that error message on the 'merchant_show_page' but need to pass the error object from the 'reviews_controller' back to the 'merchants_controller.'
From what I have read, it seems like the error object has some 'automagic' functionality when being passed between actions within the same controller. Specifically, it seems like the model knows the action that submitted the data and returns the error object using 'render' without having to initialize any instance variables on the page.
A few of the solutions that I have seen regarding similar questions:
1. Move the form submission action into the 'merchants_controller' and lose the
'reviews_controller' altogether.
2. Use 'redirect_to' instead of 'render' and pass the error object as a variable in the
redirect call.
It seems like there has to be an easy, 'rails way' to do this. Having separate controllers seems to make logical sense and it allows me to keep merchants and reviews as separate resources which, from what I have read, is the proper rails convention I should be shooting for. Is this just an issue where there is not a proper 'rails way' to do it? If so, what other rails convention should I be defaulting to?
Any help that can be given would be much appreciated.
Thanks for your help,
noob :)
Related
I have a has_many and belongs_to association in my Rails app. An OfficeAddress belongs_to Address, so my problem right now is how to build it on the form. When I create a new office address, to associate it into my address, the address should be created already. On my office_address_controller I have this.
class OfficeAddressesController < ResourceController
def index
#office_address = Spree::OfficeAddress.all
end
def new
new_address = Spree::Address.new
#new_office_address = new_address.office_address.build
end
def create
p params
end
end
and on my office address new.html.erb is currently empty because I can't find any documentation on how to build a form. I'd be interested in examples or documentation. Also the controller build confuses me. It didn't throw any error I was expecting error as new_address doesn't have any Id yet.
So I don't know if this is the right answer but this works for me. So here's my code.
Controller:
def new
#new_office_address = Spree::OfficeAddress.new
end
def create
b = Spree::Address.create(permit_params_address)
c = Spree::OfficeAddress.create(address_id: b.id)
if b.save && c.save
redirect_to admin_office_addresses_path
else
p b.errors.messages
end
end
View, new.html.erb
<%= form_for [:admin, #new_office_address], html: { autocomplete: "off" } do |ofc_add_form| %>
<fieldset data-hook="new_property">
<div class="row">
<div class="col-md-12">
<%= fields_for :address, Spree::Address.default do |adds|%>
<%= render :partial => 'spree/admin/shared/address_form', :locals => { :f => adds, :type => "New Office Address" } %>
<%end%>
</div>
</div>
<div class="form-actions">
<%= render partial: 'spree/admin/shared/new_resource_links' %>
</div>
</fieldset>
<% end %>
I was doing the right thing at first but I forgot to Spree::Address.default on fields_for I don't really know what it does but on the rails syntax, it accepts an object.
on my controller new method, after creating the address, to relate them with each other (OfficeAdddress) I just grab the Id from the address that was created and add it on my office address. I'm not sure this is the best way to solve the problem. I'm open for changes. I'm also adding more validation on my new method
I'm in need of some help regarding setting up the routing (I think that is the problem) for a view that is set up with bootstrap tabbed navigation. What I want to do is set up a form in each tab. Sounds easy enough.
However I can't figure out how the routing works when you want to submit two different actions (one to save to the db, and the other to get info from the db), each from their own tab. I can get it all to work perfectly if I give the "get_users" controller action a view, but when I try to bring it back together on the same page, just in different tabs, it goes a little awry and I'm not sure how to route correctly.
The two controller actions I have are:
class Users::RegistrationsController < Devise::RegistrationsController
def user_accounts
#user = User.new
end
def get_users
#users = User.search(params[:search]).paginate
(:per_page => 20, :page => params[:page] )
end
end
EDIT ------------------------------------
Include routes.rb - excluding the extra devise related routes. Can get the create_internal_user action working not the other. I understand that the routes are
incorrect as rails could never understand them. The first is correct, the second is simply what I would imagine it to look like if it were possible
# Authentication
as :user do
#add new internal users
get '/useraccounts' => 'users/registrations#user_accounts', as: 'new_internal_user'
post '/useraccounts' => 'users/registrations#create_internal_user', as: 'internal_user'
**# searching users - this is where I am uncertain**
get '/useraccounts' => 'users/registrations#get_users', as: 'get_internal_user'
post '/useraccounts' => 'users/registrations#get_users', as: 'getting_internal_user'
# other devise routes here
end
The view that renders them looks like this and renders two partials:
<div class="container">
<h1>User Accounts</h1>
<ul class="nav nav-tabs">
<li class="active">Add User</li>
<li>Edit User</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div class="tab-pane fade in active" id="adduser">
<%= render 'users/registrations/create_internal' %>
</div>
<div class="tab-pane fade in" id="getusers">
<%= render 'users/registrations/get_users' %>
</div>
</div>
</div>
The two partials
_create_internal.html.erb
<%= form_for(resource, as: resource_name,
url: new_internal_user_path(resource_name)) do |f| %>
<div><%= f.label :email %><br />
<% for role in User::ROLES %>
<%= check_box_tag "user[roles][#{role}]", role,
#user.roles.include?(role),
{:name => "user[roles][]"}%>
<%= label_tag "user_roles_#{role}", role.humanize %><br />
<% end %>
<%= hidden_field_tag "user[roles][]", "" %>
<div class="col-md-10 center"><%= f.submit "Create User",
class: "btn btn-primary"%></div>
<% end %>
_get_users.html.erb
<%= form_tag({:controller => "users/registrations", :action => "get_users"},
method: :get) do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<div class="center" id="users">
<% if defined?(#users) %>
<%= render 'users/registrations/searchresults' %></div>
<%= will_paginate #users %>
<% end %>
I've spent many many hours trying to figure this out and just haven't been able to. Very new to Rails so this may be an easy question for someone. I've had a look at using AJAX, and resourceful routing, but as I'm still new I'm finding it a bit tough to wrap my head around it all at the one time. This is my first post on SO so apologies if I'm missing anything (please let me know).
Let's say your controller is UsersController and assuming your routes are not nested, this is how you create named routes:
get '/useraccounts', to: 'users#get_users' ## this route maps to the get_users action
post '/useraccounts' to: 'users#create_internal_user' # this route maps to the create_internal_user action
EDIT:
the format is controller_name#action_name. So make sure to replace users with your controllers name in plural
UPDATE:
if your controller's name is RegistrationsController
try:
get '/useraccounts', to: 'registrations#get_users'
post '/useraccounts' to: 'registrations#create_internal_user'
Looking at it, I think your error will likely be from your _get_users.html.erb partial.
Firstly, you're using form_for - why? Your use of form_for basically means you have to use the data inside #user to make it work, which I think will be causing the problem
I would use a form_tag as this does not persist your data:
<%= form_tag({:controller => "users/registrations", :action => "get_users"}, method: :get) do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
<% end %>
<% if defined?(#users) #-> this might need to be passed as a local var %>
<%= render 'users/registrations/searchresults' %></div>
<%= will_paginate #users %>
<% end %>
Routes
Your other problem is to do with your routes:
get '/useraccounts' => 'users/registrations#user_accounts', as: 'new_internal_user'
post '/useraccounts' => 'users/registrations#create_internal_user', as: 'internal_user'
**# searching users - this is where I am uncertain**
get '/useraccounts' => 'users/registrations#get_users', as: 'get_internal_user'
post '/useraccounts' => 'users/registrations#get_users', as: 'getting_internal_user'
Tell me how you expect Rails to know the difference between get '/useraccounts and get /useraccounts? The fact is it can't
You'll need to split up the routes so they don't all use the same path. I would do this:
get '/useraccounts' => 'users/registrations#user_accounts', as: 'new_internal_user'
post '/useraccounts' => 'users/registrations#create_internal_user', as: 'internal_user'
**# searching users - this is where I am uncertain**
get '/search' => 'users/registrations#get_users', as: 'get_internal_user'
After much frustration I have solved my own problem. With help from Rich Peck I realised that I was essentially creating a view with two forms on it, and that the tabbed navigation didn't really mean anything to the problem.
So I really only needed the two routes:
# #add new internal users
get '/useraccounts' => 'users/registrations#user_accounts', as: 'user_accounts'
post '/useraccounts' => 'users/registrations#create_internal_user', as:
'create_internal_user'
And then in the controller just changed the user_accounts action to look like this:
def user_accounts
#user = User.new
if params[:search_button]
#users = User.search(params[:search]).paginate(:per_page => 15, :page => params[:page] )
end
end
Came about this discovery thanks to this question/answer here:
form_for with multiple controller actions for submit
In the end the problem wasn't what I thought it was and ended up being simple. Was certainly an interesting learning journey. Thanks for your help again Rich Peck.
I've seen this question asked everywhere, but it never solves my problem. Heres my controller:
class UserVacationDaysController < ApplicationController
def new
#user = current_user
#user_vacation_days = UserVacationDay.new
end
def create
#user_vacation_days = UserVacationDay.create(params[:user_vacation_day])
#user_vacation_days.user = current_user
# #user_vacation_days.calculate_work_days
# (another param that holds date range will get passed in)
# puts #user_vacation_days.errors.inspect
if #user_vacation_days.persisted?
flash[:notice] = "Request Sent"
redirect_to dashboard_index_path
request_vacation_days # method from model. model method calls method in employee_mailer
else
flash[:notice] = "Something went wrong, please try again"
render :new
end
end
end
And here is my view (form).
<h2>Request Days Off</h2>
<%= form_for :user_vacation_days, :url => user_vacation_days_path do |f| %>
<div><%= f.label "How much time off would you like to take?" %>
<%= f.number_field :number_of_days %></div>
<div><%= f.label "Argue your case, slave" %>
<%= f.text_area :description %></div>
<div><%= f.submit "Request Time Off" %></div>
<% end %>
The routes for my 2 controller methods are
user_vacation_days POST /user_vacation_days(.:format) user_vacation_days#create
new_user_vacation_day GET /user_vacation_days/new(.:format) user_vacation_days#new
Does anyone have any idea what's going on? I've looked all over the place, and I can't find anything. I can't think of any reason why the controller method wouldn't be found. Thanks!
Instead of <%= form_for :user_vacation_days, :url => user_vacation_days_path do |f| %> what happens if you use <%= form_for #user_vacation_days, :url => user_vacation_days_path do |f| %>
Also, does a User have_many VacationDay? You might want to change to resourceful routes, and have vacation days nested.
config/routes.rb
resources :users do
resources :user_vacation_days
end
in your new action under UserVacationDaysContoller #may want to rename this to just VacationDays since the nesting implies
def new
#user = current_user
#user_vacation_days = #user.vacation_days.build
end
I decided to start a little project in rails 3 and I am a little bit stuck on a form... Where can I specified the f.submit action should go to a special controller / action ?
The code in the form is:
<%= form_for #user, :url => { :action => "login" } do |f| %>
<div class="field">
<%= f.text_field :email %><br />
<%= f.text_field :password %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
User is defined as #user = User.new in "index" method of "home_controller".
but I have the error:
No route matches {:controller=>"home", :action=>"login"}
as soon as I run http://0.0.0.0:3000
I am very sorry for this newbee question but I cannot find the routing details (I worked a little bit with rails a couple of years ago but...)
Thanks,
Luc
You don't need to specify any action for f.sumbit.
First of all, you need to make sure you put
resources :users
(for example)
in your routes.rb
then if you want to create a user
put
def new
#user = User.new
end
in your users_controller so you have a page to create new user
or you can put #user=User.new anywhere you like, remember to set
the route correctly
then
def create
#user = User.new(params[:id])
if #user.save
sign_in #user
redirect_to #user
else
render 'new'
end
end
is the part that does real work after you hit on submit
the actual part that connect your form with everything else is this line
<% form_for #user do |f| %>
you can change user to other object, and you can also edit form using update action in a controller.
Hope you got the idea
Whenever you use REST objects, the mere:
form_for #article
is enough for the form to find the proper path.
Otherwise, you can use helpers this way:
form_tag(:controller => "people", :action => "search", :method => "get", :class => "nifty_form")
More info here: http://edgeguides.rubyonrails.org/form_helpers.html
Ok guys, so I'm making a scheduler.
So I have two tables so far,
Shows with "title:string" and "description:text" and I also have
ShowTime; with "show_id:integer", "day:string", and "show_time:time".
I did the has_many, and belongs_to, I honestly do not know where to go from here on,
I want a user to be able to add the times when creating a new show. What would I do? I was looking at some rails associations documentations, seems like I would be making something like,
#showtime = #shows.showtimes.create(:show_id => show.id, :day => DAY, :show_time => TIME)
Notice I put just DAY and TIME, because I also honestly don't know how I'll fetch this data.
It really depends on your interface. But for simplicity, let's assume you provided two select boxes for selecting day and time, and have to add ShowTime one by one.
And assume you have rest resources:
map.resources :shows do |show|
show.resources :show_times
end
The form: (given a #show object created already)
<% form_for #show_time, :url => show_show_time_path(#show) do |form| %>
<%= form.label :day %>: <%= form.select :day, [["Mon", "mon"], ["Tue", "tue"]], {} %>
<%= form.label :show_time %>: <%= form.select :show_time, [["Slot 1", "09:00"]], {} %>
<% end %>
You should provide your best way to generate the day & show_time arrays. They are in the following structure:
[["Text", "value"], ["Text", "value"]]
which will generate something like:
<option value="value">Text</option>
After the form is submitted, in your create action:
def create
#show = Show.find params[:show_id] # this params[:show_id] is from the rest resource's path
#show_time = #show.show_times.build(params[:show_time]) # this params[:show_time] is from the form you submitted
if #show_time.save
flash[:notice] = "Success"
redirect_to show_show_time_path(#show, #show_time)
else
flash[:notice] = "Failed"
render :action => "new"
end
end