Here is what I have in my view:
<%= simple_form_for :artist, :url => url_for(:action => 'upvote', :controller => 'artists'),
:method => 'post' do |f| %>
<%= f.input :choose_an_artist, :selected => "first artist", collection: [["first artist", 1], ["second artist", 2], ["third artist", 3], ["fourth artist", 4]] %>
<%= f.submit "Vote" %>
<% end %>
My ArtistsController:
def upvote
#artist = Artist.find(params[:choose_an_artist])
#artist.liked_by current_user
respond_to do |format|
format.html {redirect_to :back }
end
end
routes.rb:
resources :artists do
member do
put "like", to: "artists#upvote"
end
end
I am getting the following error:
No route matches {:action=>"upvote", :controller=>"artists"}
What could be causing this? How do I get this to work so that the user can select an artist from a collection and vote for that artist?
There are several issues in your code:
First, you defined your route as PUT and you are forcing
simple_form to produce a POST form. Change method: :post
to method: :put in your view and you should be all set.
Second, you need to define your route according to your controller and action name:
resources :artists do
member do
put :upvote
end
end
Third, you defined your route as on: :member. That means that it needs an artist_id to generate. In your setup, you need to define the route on: :collection. I'd also better use the route path method instead of url_for, it's way easier to spot this errors.
resources :artists do
collection do
put :upvote
end
end
And change the url_for part for update_artists_path (if that's the correct route from rake routes).
Another issue not related to your question: :choose_an_artist is not an attribute defined in Artist model. This will cause another error when rendering the form.
I'd either rename that per the actual attribute name you are selecting, :id and change the controller accordingly (my choice), or change the form helper from f.input to a non-model related select_tag and keep the names as they are.
Related
I have a two-part question about form_for and nested resources. Let's say I'm writing a blog engine and I want to relate a comment to an article. I've defined a nested resource as follows:
map.resources :articles do |articles|
articles.resources :comments
end
The comment form is in the show.html.erb view for articles, underneath the article itself, for instance like this:
<%= render :partial => "articles/article" %>
<% form_for([ :article, #comment]) do |f| %>
<%= f.text_area :text %>
<%= submit_tag "Submit" %>
<% end %>
This gives an error, "Called id for nil, which would mistakenly etc." I've also tried
<% form_for #article, #comment do |f| %>
Which renders correctly but relates f.text_area to the article's 'text' field instead of the comment's, and presents the html for the article.text attribute in that text area. So I seem to have this wrong as well. What I want is a form whose 'submit' will call the create action on CommentsController, with an article_id in the params, for instance a post request to /articles/1/comments.
The second part to my question is, what's the best way to create the comment instance to begin with? I'm creating a #comment in the show action of the ArticlesController, so a comment object will be in scope for the form_for helper. Then in the create action of the CommentsController, I create new #comment using the params passed in from the form_for.
Thanks!
Travis R is correct. (I wish I could upvote ya.) I just got this working myself. With these routes:
resources :articles do
resources :comments
end
You get paths like:
/articles/42
/articles/42/comments/99
routed to controllers at
app/controllers/articles_controller.rb
app/controllers/comments_controller.rb
just as it says at http://guides.rubyonrails.org/routing.html#nested-resources, with no special namespaces.
But partials and forms become tricky. Note the square brackets:
<%= form_for [#article, #comment] do |f| %>
Most important, if you want a URI, you may need something like this:
article_comment_path(#article, #comment)
Alternatively:
[#article, #comment]
as described at http://edgeguides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objects
For example, inside a collections partial with comment_item supplied for iteration,
<%= link_to "delete", article_comment_path(#article, comment_item),
:method => :delete, :confirm => "Really?" %>
What jamuraa says may work in the context of Article, but it did not work for me in various other ways.
There is a lot of discussion related to nested resources, e.g. http://weblog.jamisbuck.org/2007/2/5/nesting-resources
Interestingly, I just learned that most people's unit-tests are not actually testing all paths. When people follow jamisbuck's suggestion, they end up with two ways to get at nested resources. Their unit-tests will generally get/post to the simplest:
# POST /comments
post :create, :comment => {:article_id=>42, ...}
In order to test the route that they may prefer, they need to do it this way:
# POST /articles/42/comments
post :create, :article_id => 42, :comment => {...}
I learned this because my unit-tests started failing when I switched from this:
resources :comments
resources :articles do
resources :comments
end
to this:
resources :comments, :only => [:destroy, :show, :edit, :update]
resources :articles do
resources :comments, :only => [:create, :index, :new]
end
I guess it's ok to have duplicate routes, and to miss a few unit-tests. (Why test? Because even if the user never sees the duplicates, your forms may refer to them, either implicitly or via named routes.) Still, to minimize needless duplication, I recommend this:
resources :comments
resources :articles do
resources :comments, :only => [:create, :index, :new]
end
Sorry for the long answer. Not many people are aware of the subtleties, I think.
Be sure to have both objects created in controller: #post and #comment for the post, eg:
#post = Post.find params[:post_id]
#comment = Comment.new(:post=>#post)
Then in view:
<%= form_for([#post, #comment]) do |f| %>
Be sure to explicitly define the array in the form_for, not just comma separated like you have above.
You don't need to do special things in the form. You just build the comment correctly in the show action:
class ArticlesController < ActionController::Base
....
def show
#article = Article.find(params[:id])
#new_comment = #article.comments.build
end
....
end
and then make a form for it in the article view:
<% form_for #new_comment do |f| %>
<%= f.text_area :text %>
<%= f.submit "Post Comment" %>
<% end %>
by default, this comment will go to the create action of CommentsController, which you will then probably want to put redirect :back into so you're routed back to the Article page.
How do I set the url for a "semantic form" tag in an activeAdmin custom page partial for collection_action in my activeAdmin controller?
I have:
item.rb
ActiveAdmin.register Item, :as => "MyItems" do
menu :parent => "My", :label => "My Items"
collection_action :add_me, :method => :post do
redirect_to "/" # just for testing
end
end
custom page ActiveAdmin controller
ActiveAdmin.register_page "MyItemsCustomPage" do
content do
#items = Item.all
render "item", { :items => #items }
end
end
_item.html.erb (for custom page)
<%= semantic_form_for :item_add_me, :url => add_me_admin_items_path do |f| %>
<%= f.buttons :commit %>
<% end %>
And after going to the custom page I have the error:
undefined local variable or method `add_me_admin_items_path' for #<#<Class:0x00000006c3ff40>:0x00000005f8bd80>
btw, semantic form for admin_items_path works well for item add action.
PS. If I change the url to /admin/items/add_me and set the :method to :post, I get the routing error: No route matches [POST] "/admin/items/add_me"
Found the problem.
After removing :as => "MyItems" in item.rb:
ActiveAdmin.register Item do
All works ok.
The problem here is that ActiveAdmin.register Item, as: "MyItems" actually renames all your routes to be my_items instead of my_item in all the method names. So, in your form, instead of using add_me_admin_items_path, you could have used add_me_admin_my_items_path.
I have a very simple render that goes as follow:
<%= form_for(:relationships, :url => relationships_path, :html => {:method => 'delete'}) do |f| %>
<div><%= f.hidden_field :user_id_to_unfollow, :value => #user.id %></div>
<div class="actions"><%= f.submit "Unfollow" %></div>
<% end %>
When I submit this form it will always give me a
Routing Error
No route matches "/relationships"
on my page.
In my relationships controller, I have created all the propers methods:
def create
...
end
def destroy
...
end
def update
...
end
def show
...
end
And in my routes config I have made sure to allow all routes for the relationships controller
resources :relationships
But I can't seem to get into the destroy method of the controller :(
However if I remove the
:html => {:method => 'delete'}
method parameter in the form_for then I get to the create method of the controller no pb.
I don't get it....
Alex
ps: this is the rake routes results for relationships:
relationships GET /relationships(.:format) {:action=>"index", :controller=>"relationships"}
POST /relationships(.:format) {:action=>"create", :controller=>"relationships"}
You should point the delete request to single resource url eg. relationships/4325. Run rake routes to view what url/verb combinations are valid.
--edit
Routes for relationship resources:
resources :relationships, :only => [:index, :create, :destroy]
Unfollow button (creates a form for itself):
= button_to "Unfollow", relationship_path(relationship), :method => 'delete'
In 'app/views/users/reset.html.erb' file I have this code:
<%= form_tag( send_reset_users_path, :method => :post ) do %>
<%= text_field_tag :email %>
<%= submit_tag("Send") %>
<% end %>
In 'app/controllers/*users_controller.rb*' I have this code:
def reset
respond_to do |format|
format.html # reset.html.erb
end
end
def send_reset
...
end
In 'config/routes.rb' I have this code:
resources :users do
collection do
get 'reset'
get 'send_reset'
end
end
When I submit the form I get the error: "No route matches "/users/send_reset"" (browser URL becomes '.../users/send_reset'). What is wrong? How can I "map" URLs to Rails actions?
P.S.: I think the problem is in "config/routes.rb"...
the problem is here :method => :post and get 'send_reset', in my opinion you are trying to POST parameters when your conntroller expect GET method
You routes.rb declares the send_reset route as only available via get. You have to write post 'send_reset':
resources :users do
collection do
get 'reset'
post 'send_reset'
end
end
I have a two-part question about form_for and nested resources. Let's say I'm writing a blog engine and I want to relate a comment to an article. I've defined a nested resource as follows:
map.resources :articles do |articles|
articles.resources :comments
end
The comment form is in the show.html.erb view for articles, underneath the article itself, for instance like this:
<%= render :partial => "articles/article" %>
<% form_for([ :article, #comment]) do |f| %>
<%= f.text_area :text %>
<%= submit_tag "Submit" %>
<% end %>
This gives an error, "Called id for nil, which would mistakenly etc." I've also tried
<% form_for #article, #comment do |f| %>
Which renders correctly but relates f.text_area to the article's 'text' field instead of the comment's, and presents the html for the article.text attribute in that text area. So I seem to have this wrong as well. What I want is a form whose 'submit' will call the create action on CommentsController, with an article_id in the params, for instance a post request to /articles/1/comments.
The second part to my question is, what's the best way to create the comment instance to begin with? I'm creating a #comment in the show action of the ArticlesController, so a comment object will be in scope for the form_for helper. Then in the create action of the CommentsController, I create new #comment using the params passed in from the form_for.
Thanks!
Travis R is correct. (I wish I could upvote ya.) I just got this working myself. With these routes:
resources :articles do
resources :comments
end
You get paths like:
/articles/42
/articles/42/comments/99
routed to controllers at
app/controllers/articles_controller.rb
app/controllers/comments_controller.rb
just as it says at http://guides.rubyonrails.org/routing.html#nested-resources, with no special namespaces.
But partials and forms become tricky. Note the square brackets:
<%= form_for [#article, #comment] do |f| %>
Most important, if you want a URI, you may need something like this:
article_comment_path(#article, #comment)
Alternatively:
[#article, #comment]
as described at http://edgeguides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objects
For example, inside a collections partial with comment_item supplied for iteration,
<%= link_to "delete", article_comment_path(#article, comment_item),
:method => :delete, :confirm => "Really?" %>
What jamuraa says may work in the context of Article, but it did not work for me in various other ways.
There is a lot of discussion related to nested resources, e.g. http://weblog.jamisbuck.org/2007/2/5/nesting-resources
Interestingly, I just learned that most people's unit-tests are not actually testing all paths. When people follow jamisbuck's suggestion, they end up with two ways to get at nested resources. Their unit-tests will generally get/post to the simplest:
# POST /comments
post :create, :comment => {:article_id=>42, ...}
In order to test the route that they may prefer, they need to do it this way:
# POST /articles/42/comments
post :create, :article_id => 42, :comment => {...}
I learned this because my unit-tests started failing when I switched from this:
resources :comments
resources :articles do
resources :comments
end
to this:
resources :comments, :only => [:destroy, :show, :edit, :update]
resources :articles do
resources :comments, :only => [:create, :index, :new]
end
I guess it's ok to have duplicate routes, and to miss a few unit-tests. (Why test? Because even if the user never sees the duplicates, your forms may refer to them, either implicitly or via named routes.) Still, to minimize needless duplication, I recommend this:
resources :comments
resources :articles do
resources :comments, :only => [:create, :index, :new]
end
Sorry for the long answer. Not many people are aware of the subtleties, I think.
Be sure to have both objects created in controller: #post and #comment for the post, eg:
#post = Post.find params[:post_id]
#comment = Comment.new(:post=>#post)
Then in view:
<%= form_for([#post, #comment]) do |f| %>
Be sure to explicitly define the array in the form_for, not just comma separated like you have above.
You don't need to do special things in the form. You just build the comment correctly in the show action:
class ArticlesController < ActionController::Base
....
def show
#article = Article.find(params[:id])
#new_comment = #article.comments.build
end
....
end
and then make a form for it in the article view:
<% form_for #new_comment do |f| %>
<%= f.text_area :text %>
<%= f.submit "Post Comment" %>
<% end %>
by default, this comment will go to the create action of CommentsController, which you will then probably want to put redirect :back into so you're routed back to the Article page.