user7055375
user7055375

Reputation:

How do I create a Rails form that submits to a RESTful action?

With Rails 5, how do I create a form that submits to a RESTful action?

In my routes file I have:

  resources :people do
    collection do
      get 'image/:id', :to => "people#image", :as => 'image'
      get 'ranks', :to => "people#ranks", :as => 'ranks'
      get 'search/:search(.:format)', to: 'people#search'
    end
  end

So I'm wondering how to construct a form that submits to the "search" action.

I tried the following:

<%= form_tag(people_search_path) do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search' %>
<% end %>

But this results in the following error:

undefined local variable or method `people_search_path' for #<#<Class:0x00007ff2afc59168>:0x00007ff2afc50f68>

Upvotes: 1

Views: 166

Answers (2)

Muhammed Anees
Muhammed Anees

Reputation: 1850

You should use search_people_path instead of people_search_path. But I don't think that change will take you to what you are trying to achieve. Since your route is
get 'search/:search(.:format)', to: 'people#search'
rails will expect you to provide a value for the keyword :search to build a url like /people/search/yoursearchterm with something like search_people_path(yoursearchterm). For your functionality to work, change the form to

<%= form_tag search_people_path, method: :get do %>
  <%= text_field_tag :search %>
  <%= submit_tag 'Search' %>
<% end %>

and then in your controller

def search
  params[:search]
end


Also the routes to

get 'search', to: 'people#search'

Updated

Since you are trying to submit the form directly to a rest route like search/:search, I believe there is no direct rails way to achieve the same because that's how forms work. But if all you want is just a rest route like yourwebsite.com/search/yoursearchterm when you submit the form, there are some workarounds to achieve this. Either you have to write a piece of js on submitting the form and forward the request to your route with the input value or you can perform a redirect in the controller to the route, like this.
Routes:

resources :people do
    collection do
      get 'search', to: 'people#search_people'
      get 'search/:search', to: 'people#search'
    end
  end

So, first the form will be submitted to search_people action and then redirect to search with params. In controller

def search_people
    redirect_to search_people_path(params[:search])
end

This will then give you a url yoursite.com/people/search/searchterm and then in your search action, you can use the search params.

Hope this helps.

Upvotes: 0

jvillian
jvillian

Reputation: 20263

If you do rake routes, you'll see that your search path has no name:

image_people GET    /people/image/:id(.:format)        people#image
ranks_people GET    /people/ranks(.:format)            people#ranks
             GET    /people/search/:search(.:format)   people#search
      people GET    /people(.:format)                  people#index
             POST   /people(.:format)                  people#create
  new_person GET    /people/new(.:format)              people#new
 edit_person GET    /people/:id/edit(.:format)         people#edit
      person GET    /people/:id(.:format)              people#show
             PATCH  /people/:id(.:format)              people#update
             PUT    /people/:id(.:format)              people#update
             DELETE /people/:id(.:format)              people#destroy

If, instead, you do:

resources :people do
  collection do
    get  'image/:id', :to => "people#image", :as => 'image'
    get  'ranks', :to => "people#ranks", :as => 'ranks'
    get  'search/:search(.:format)', to: 'people#search', as: :search
  end
end

You'll see your path now has a name:

  image_people GET    /people/image/:id(.:format)         people#image
  ranks_people GET    /people/ranks(.:format)             people#ranks
 search_people GET    /people/search/:search(.:format)    people#search
        people GET    /people(.:format)                   people#index
               POST   /people(.:format)                   people#create
    new_person GET    /people/new(.:format)               people#new
   edit_person GET    /people/:id/edit(.:format)          people#edit
        person GET    /people/:id(.:format)               people#show
               PATCH  /people/:id(.:format)               people#update
               PUT    /people/:id(.:format)               people#update
               DELETE /people/:id(.:format)               people#destroy

Which you can use as search_people_path.

BTW, I believe that (.:format) bit is unnecessary and you can simply do:

post 'search/:search', to: 'people#search', as: :search

If I were you, however, I would do:

resources :people do
  collection do
    get  'image/:id', :to => "people#image", :as => 'image'
    get  'ranks', :to => "people#ranks", :as => 'ranks'
    get  :search, to: 'people#search', as: :search
  end
end

Which will give you:

  image_people GET    /people/image/:id(.:format)         people#image
  ranks_people GET    /people/ranks(.:format)             people#ranks
 search_people GET    /people/search(.:format)            people#search
        people GET    /people(.:format)                   people#index
               POST   /people(.:format)                   people#create
    new_person GET    /people/new(.:format)               people#new
   edit_person GET    /people/:id/edit(.:format)          people#edit
        person GET    /people/:id(.:format)               people#show
               PATCH  /people/:id(.:format)               people#update
               PUT    /people/:id(.:format)               people#update
               DELETE /people/:id(.:format)               people#destroy

Then, I would update your form as Anees Muhammed suggests (I used :terms instead of :search):

<%= form_tag search_people_path, method: :get do %>
  <%= text_field_tag :terms %>
  <%= submit_tag 'Search' %>
<% end %>

Then, when you submit, you should get something like:

Started GET "/people/search?terms=foo" for ::1 at 2018-01-29 13:49:40 -0800
Processing by People#search as HTML
  Parameters: {"utf8"=>"✓", "terms"=>"foo", "commit"=>"Search"}

And you can access your terms by doing params[:terms].

There is, BTW, nothing "non-RESTful" about this. It is, IMO, completely consistent the Guide for adding RESTful actions.

Upvotes: 2

Related Questions