How to use form_for helper in rails? - ruby-on-rails

New to rails. I am using form_for helper in rails 2.3.5. The question is, when editing a record, I cannot use:
<% form_for :myrecord, :url => {:action => :update} %>
...
<% end %>
But I have to use:
<% form_for #myrecord, :url => {:action => :update} %>
... # the same as above
<% end %>
When I use the first code, it always return error saying undefined method. Shouldn't the first code and the second code serves the same when "myrecord" is the name of the instance passed from controller to view?
Thank you for any hints and answers.

form_for take a block so it's more like. If this record is same that resource, the :url option is not needed. FormHelper check if #myrecord is #new_record? or not. If new_record use POST instead use PUT.
<% form_for #myrecord do |f| %>
<%= f.text_field :field %>
<% end %>

Related

rails, form_for in a secon route, but on the same model

In a rails 4 application, I have a book resource, that is a Book model with its controller, views and route. It's what gets created by:
rails g scaffold book title
Now I want to have another set of views (and another controller) that allows to manage the same model, maybe dedicated to a different user.
I want both the creating function and the editing function to be available on this different route and view, .
Let's call it book2.
The views in the /book2 url should operate on the Book2sController.
form_for support
But the form_for guesses the submit route (and puts it in the action attribute) from the model class, that, being it always Book, lets rails guess that the submit url is /books/1 for edit or /books/ for new and not /book2s/1 for edit and /book2s/ for new as it should be.
So i found this solution, but i find it to be a bit cumbersome.
Is there anything better out there?
<%= form_for #book, :url => #book.new_record? ? url_for(book2s_path) : url_for(book2_path(#book)) do |f| %>
<%= f.text_field :title %>
<% end %>
You could set the url in your controller.
def new
# ...
#form_url = book2s_path
# ...
end
def edit
# ...
#form_url = book2_path(#book)
# ...
end
Then your view becomes:
<%= form_for #book, :url => #form_url do |f| %>
<%= f.text_field :title %>
<% end %>
I have also seen:
<%= form_for #book, :url => {:controller => 'book2s', :action => #action} do |f| %>
<%= f.text_field :title %>
<% end %>
and you just set #action in the controller (probably create or update).
Note that you don't need to include the url_for like you have.

What are the differences between form_for #project_profile and form_for :project_profile

I just started to learn rails. My rails version is 3.0.7. I am wondering what are the differences between <% form_for :project_profile %> and <% form_for #project_profile %>. I have this question because I went into the following situation:
If I use <% form_for :project_profile %>, it doesn't give me an error, but the form is actually not working.
If I use <% form_for #project_profile %>, I will get an error: undefined method `project_profile_path' for #<#:0x00000103546d80>
If I use <%= form_for #project_profile, :url => "/projects/#{params[:project_id]}/profile/update" do |f| %>, it will work but the code is ugly.
You can refer to the following codes to understand the context of my problem better.
I have a project model and a project_profile model. One project has one project_profile.
The following two lines are from my routes.rb.
match '/projects/:project_id/profile/edit' => "project_profiles#edit"
match '/projects/:project_id/profile/update' => "project_profiles#update"
This is from my project_profiles_controller.rb
class ProjectProfilesController < ApplicationController
def edit
#project_profile = Project.find(params[:project_id]).project_profile
end
def update
#project_profile = Project.find(params[:project_id]).project_profile
respond_to do |format|
if #project_profile.update_attributes(params[:project_profile])
format.html {}
else
format.html { render :action => "edit" }
end
end
end
end
The following code is from _form.html.erb
<%= form_for #project_profile, :url => "/projects/#{params[:project_id]}/profile/update" do |f| %>
<div class="field">
<%= f.label :title %>
<br/>
<%= f.text_field :title %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You should learn about resource and nested resource routing in Rails.
The way you define controller is also not conventional. There is an article on Rails Guides on Getting Started section that covers that too.
Basically spoken, form_for #project_profile is an advanced (resource-oriented), nowadays preferred style. If you want to dig a little deeper into this, the API itself explains the difference pretty well.
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for
cheers

rails admin edit view path routes

I've got an admin section setup, but am having trouble getting the "update" route to work.
Getting error when hitting "update" via the edit view:
"No action responded to 2."
For some reason the route is responding to the :id as the :action.
Parameters:
Parameters: {"commit"=>"Update", "action"=>"2", "_method"=>"put", "admin"=>{"ended_at(1i)"=>"2010", "ended_at(2i)"=>"8", "ended_at(3i)"=>"22"}, "id"=>"edit", "controller"=>"admin/subscriptions"}
The edit view uri:
/admin/subscriptions/2/edit
Edit view:
<% form_for :admin, #subscription, :html => {:method => :put} do |f| %>
<p>
<%= f.label :ended_at %><br />
<%= f.date_select :ended_at %>
</p>
<p>
<%= f.submit 'Update' %>
</p>
<% end %>
Route:
map.namespace :admin do |admin|
admin.resources :subscriptions
end
I assume I need to do something differently in the form_for method or maybe the routes, but everything I've tried isn't working.
Thanks for the help!!
It should be this:
<% form_for [:admin, #subscription] do |f| %>
By putting :admin and #subscription in square-brackets, this makes it into an array which is passed as the first argument to form_for. The benefit of this is if #subscription is a pre-existing record (as-in, one found by find, not created with new) then Rails will know to set the form method to PUT.
This works:
<% form_for :admin, #subscription, :html => {:method => :put}, :url => { :action => "update" } do |f| %>
Seems verbose though. Any better ideas?
Try
- form_for :subscription, #subscription do |f|
We're using formtastic here.

form_for is giving me the wrong url for path_prefixes

I am having a problem with the form_for method in Rails. It is behaving strangely.
I have a route with a path prefix, something like:
map.resources :beers, :path_prefix => '/:brewery'
And I have a form like this (#beer.brewery is a string, just the name of the brewery):
<% form_for #beer, :url => { :brewery => #beer.brewery } do |form|
--some fields
<% end %>
It will set the action of the form to this for a new record.
/brewery_name/beers/new
and this for an existing record.
/brewery_name/beers/1/edit
Anyone knows why this happens or how to fix it?
--edit--
Right now I am solving this like this (for a new record):
<% form_for #beer, :url => beers_path(#beer.brewery) do |form| %>
and (for an edited record)
<% form_for #beer, :url => beer_path(#beer.brewery, #beer) do |form| %>
But I want to do it the same way for new and edit, if it is possible.
Cheers,
Thijs.
I'm guessing you want this to go to a brewery's beer. In that case:
<% form_for [#beer.brewery, #beer] do |f| %>
-- some fields
<% end %>
By providing an Array as the first argument to form_for it will generate a nested resource.

Multiple links on a Rails view that each perform different actions - how best to handle this?

One of the things I'm doing includes several links on the show view. For instance, I have a link (or button) for "Accepting", and another one for "Rejecting". Click on Accept, and the model updates the is_accepted field as true, click on Reject, and the is_accepted field is false.
Now, how best do I handle this? In ASP.NET, I would have simply created a LinkButton and written a handler, but Rails doesn't work that way, so I'm trying to figure out how to essentially replicate what a LinkButton would do.
Right now, I'm coding two forms on the same view, nearly identical, that look like this:
<%= form_for #thing do |f| %>
<%= hidden_field_tag 'thing[is_accepted]', '1' %>
<%= f.submit "Accept" %>
<% end %>
<%= form_for #thing do |f| %>
<%= hidden_field_tag 'thing[is_accepted]', '0' %>
<%= f.submit "Reject" %>
<% end %>
This feels weird to me, but I can't seem to find anything that says this is the wrong way to do it.
I could, I assume, dry things up by using a partial and/or a helper method, but I wanted to make sure I'm on the right track and not doing something totally wrongly.
You can give your submit tag a name.. ie
<%= form_for #thing do |f| %>
<%= hidden_field_tag 'thing[is_accepted]' %>
<%= f.submit "Accept", :name => 'accept' %>
<%= f.submit "Reject", :name => 'reject' %>
<% end %>
Then you can detect the name in params[] and skip the '1'/'0' value.
I think you're going about it the right way. One way to clean up your forms is by using the model form helpers all the way through, so you'd end up with something like
<%= form_for #thing do |f| %>
<%= f.hidden_field :accepted, :value => true %>
<%= f.submit "Accept" %>
<% end %>
<%= form_for #thing do |f| %>
<%= f.hidden_field :accepted, :value => false %>
<%= f.submit "Reject" %>
<% end %>
But other than that, it looks like the right way to go about it. I would suggest against creating new methods to do this, because you're not doing anything outside of normal web requests (updating a model in this instance).
Using the submit tag as the switch and detecting it in params[] is also a good way, but I usually prefer to keep my controllers as vanilla as possible. In the end, both of these ways would end up with the same amount of 'stuff' in the UI, so whichever style you'd rather use should be fine.
Depending on how you want your UI to work you might consider link_to_remote (part of the prototype helper) - you can specify an action, params etc, and have it return some JS that gets run.
If you're using map.resources in your routes.rb you should be able to do something like this:
map.resources :things, :member => {:accept => :get, :reject => :get}
Then in your controller:
def accept
#thing = Thing.find(params[:id])
#thing.is_accepted = true
#thing.save
end
def reject
#thing = Thing.find(params[:id])
#thing.is_accepted = false
#thing.save
end
And finally in your view:
<%= link_to 'Accept', accept_thing_url(#thing) %>
<%= link_to 'Reject', reject_thing_url(#thing) %>
Or if you are using Ajax:
<%= link_to_remote 'Accept', :url => accept_thing_url(#thing) %>
<%= link_to_remote 'Reject', :url => reject_thing_url(#thing) %>

Resources