Difference between :model and #model in form_for? - ruby-on-rails

What is the difference between using form_for the following way:
<% form_for #user do |f| %>
<%= f.label :name %>:
<%= f.text_field :name, :size => 40 %>
...
<% end %>
and:
<% form_for :user, :url => {:action => 'create'} do |f| %>
<%= f.label :name %>:
<%= f.text_field :name, :size => 40 %>
...
<% end %>
Does using #user just automatically use CRUD methods for the URL actions?

If you just give a model instance like #user without specifying an action (as in your first example), Rails automatically uses the appropriate CRUD action for your form:
If #user is a new, unsaved User object, the form will point to your create action.
If #user is an existing User loaded from the database, the update action will be used instead.
This has the advantage that you can reuse the same form for both your edit and new views without changing the :url parameter for your forms.
As usual, the API docs provide more information.

If you give form_for a symbol without an instance variable it looks for an instance variable with the same name.
The documentation says:
For example, if #post is an existing
record you want to edit
<% form_for #post do |f| %>
...
<% end %>
is equivalent to something like:
<% form_for :post, #post, :url => post_path(#post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %>
...
<% end %>

Related

Submit values with link_to in Rails 5.1

So currently i have a link_to, where signed in users can click on:
<%= link_to "Enroll", [#task.project, #task] %>
The user has an association with the project, through subscription. To create a new subscription for a user with a project, i wrote some simple form for it.
<%= form_for([#project, #subzz]) do |f| %>
<%= f.hidden_field :project_id, :value => #project.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Which works fine and creates the association. However, i want that the user is able to create the subscription whenever he clicks on 'enroll' instead of a second, extra submit button.
Any ideas how to approach this? I thought about using jQuery, but not sure how to inject the ids with it and if its the 'right' way to do it.
Thanks in advance everyone!
EDIT:
When using the method posted as answer, i get:
param is missing or the value is empty: sub
My updatet form:
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
<%= hidden_field_tag :project_id, :value => #project.id %>
<%= hidden_field_tag :user_id, :value => current_user.id %>
<%= link_to "Enroll", [#task.project, #task], :onclick => "$('#project_form').submit() "%>
<% end %>
subs_controller.rb
class SubsController < ApplicationController
def create
#subz = Sub.create(sub_params)
project = #subz.project
redirect_to root_path
end
private
def sub_params
params.require(:sub).permit(:project_id, :user_id)
end
end
You can be using the existing form and link_to, just edit some like edit the dorm_tag like this
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
and remove the button into form like this one
<div class="actions">
<%= f.submit %>
</div>
and edit the link_to like this
<%= link_to 'Enroll', "", :onclick => "$('#project_form').submit()" %>
it will work
Update
You can achieve this without a form, comment out this form and edit the link like below
<%= link_to 'Enroll', subs_path(project_id: #project.id, user_id: current_user.id), method: :post %>
and the create method update like below
def create
#subz = Sub.new(sub_params)
if #subz.save
flash[:success] = 'Sub was successfully submited.'
redirect_to root_path
else
flash[:danger] = 'Sub not submited'
redirect_to request.referer
end
end
that is easier
Or if you keep before one with form then the link out from the form and the create method edit like the following
def create
#subz = Sub.new(sub_params)
if #subz.save
flash[:success] = 'Sub was successfully submited.'
redirect_to root_path
else
flash[:danger] = 'Sub not submited'
redirect_to request.referer
end
end
and the form will look like this
<%= form_for([#project, #subzz], html: {role: "form", id: "project_form"}) do |f| %>
<%= hidden_field_tag :project_id, :value => #project.id %>
<%= hidden_field_tag :user_id, :value => current_user.id %>
<% end %>
<%= link_to "Enroll", [#task.project, #task], :onclick => "$('#project_form').submit() "%>
if you confused this [#task.project, #task] on link tag then use direct link
So i came up with the following solution:
I've added the sub handling to the application_controller, so that its availiable for the project_controller. I also added the project, tasks as a reference, so that i am able to redirect to a task via the sub_controller, instead of the project_controller.
application_controller.rb
def create
#subs = Sub.new(sub_params)
project = #subs.project
taskz = project.tasks.first
if #subs.save
redirect_to [taskz.project, taskz]
end
end
private
def sub_params
params.require(:sub).permit(:project_id, :user_id)
end
Inside the show.html.erb from the project_controller, i use the old form:
<%= form_for([#project, #subz] do |f| %>
<%= f.hidden_field :project_id, :value => #project.id %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.submit "Submitted" %>
<% end %>
which works fine. Thanks for any previous help!

How can I pass input from a text_field as a parameter to create an object?

I want to take the input that the user enters in the text_field below and pass it as a parameter in the button_to that creates the object:
<%= text_field :objects, :object %>
<%= button_to("Comment!", :action => "create", :controller => "comments", :comment => {:user_id => current_user.id, :location_id=> location.id }) %>
How can I do this?
button_to is not the best tool to do what you need.
You need to use a form to receive the text_field on controller. Then you will be able to create a comment object. Example:
<%= form_for #comment do |f| %>
<%= f.text_field :text %>
<%= f.submit "Comment!" %>
<% end %>
Use form_for to create the Comment instead of button_to. The value of you text field will be available to your controller in params.

form_for calls model method without submit

I have an index page for admin part of my project
<% #reviews.each do |review| %>
<p><%= review.header %></p>
<p><%= review.body %></p>
<%= form_for [:admin, review] do |f| %>
<%= f.hidden_field :approve %>
<%= f.submit "Approve" %>
<% end %>
<%= form_for [:admin, review] do |f| %>
<%= f.hidden_field :reject %>
<%= f.submit "Reject" %>
<% end %>
<% end %>
where :approve and :reject are public instance methods in Review model.
For some reason, when I load this index page, it automatically calls review.reject method which sets corresponding is_rejected field to true. Same behavior applies to form_for with :approve if I remove form_for with :reject bit.
index action from corresponding controller is very simple
def index
#reviews = Review.all
end
I realize this must be normal behavior, but what I would expect is to call reject method only when I submit corresponding form. Is there a way to fix it? Thank you.
UPDATE
Just for the future reference (including my own): it is easier to use button_to helper for things like that
<%= button_to "Approve", { :action => "update", :id => review.id, :review => { :approve => true } }, :method => :put %>
<%= button_to "Reject", { :action => "update", :id => review.id, :review => { :reject => true } }, :method => :put %>
The form builder is calling approve and reject on your model because it's trying to determine what to set the value for the hidden fields to. One way around this would be to not use f.hidden_field and just create a hidden field that's not tied to your model. You can use hidden_field_tag instead.

Rails: Share variables between controller and view [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Passing only two variables between controller and view - best practice?
There is my action:
def list
#codes = Code.order("created_at")
#languages = Language.order('name').collect {|l| [l.name, l.coderay]}
end
There is my view(I removed some lines):
<% #codes.each do |code| %>
<div class="code">
<%= link_to code.title, :action => 'show', :id => code.id %>
<% if code.author %>
#<%= code.author %>
<% end %>
</div>
<% end %>
<%= render :partial => 'shared/error_messages', :locals => {:object => #code} %>
<%= form_for :code, :url => {:action => 'create' }, :html => {:multipart => true} do |f| %>
<%= f.text_field :title %><br />
<%= f.text_area :content %><br>
<%= f.select(:language, #languages, {:selected => 'text'}) %>
<%= f.text_field :author %><br>
<%= f.submit "Submit code" %>
<% end %>
There are 3 variables in it: #codes(list of posts), #code(current post, used in another action) and #languages.
My IDE writes:
At most two instance variables should be shared between controller and
view
This inspection warns if there are more than two instance
variables shared between a controller and a view. A controller should
only manage one instance variable, plus a second one for the
current_user variable.
Usually I share more variables between Controller and View(in PHP), sometimes 10+.
How it's done in Rails?
You can save an instance var by making languages a helper:
def languages
Language.order('name').collect {|l| [l.name, l.coderay]}
end
Its a guideline some developers follow some of the time.
But I would read up on Rails Routing a bit more. Understanding how Rails routing works should give you a better idea on how to structure your code.
http://guides.rubyonrails.org/routing.html
I modified your code a bit, not tested. But hopefully gives you some good ideas.
Controller:
def new
#code = Code.new
#codes = Code.order("created_at")
end
def create
#code = Code.new(params[:code])
if #code.save?
# Do your thing.
else
# render your :new action passing your #code variable
end
end
View:
<% #codes.each do |code| %>
<div class="code">
# Use Rails Routing - In console, type rake routes to get list of routes.
<%= link_to code.title, code_path(code.id) %> # example.
<% if code.author %>
<%= code.author %>
<% end %>
</div>
<% end %>
<%= render 'shared/error_messages', :object => #code %>
<%= form_for #code, :html => {:multipart => true} do |f| %>
<%= f.text_field :title %><br />
<%= f.text_area :content %><br>
# language_list = helper method.
<%= f.select(:language, language_list, {:selected => 'text'}) %>
<%= f.text_field :author %><br>
<%= f.submit "Submit code" %>
<% end %>

RESTful way to use form_for?

I am attempting to use form_for to implement a search form that works with a table-less Search model I created. The search form keeps triggering the 'index' action. I assume I should use 'new' to create the form and 'create' the process the search query. Looking at the log, my POST is getting changed into a GET. Here's my code:
/searches/new.html.erb:
<% form_for :searches, #search, :url => searches_path, :html => {:method => :post} do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :keywords %><br />
<%= f.text_field :keywords %>
</p>
<p><%= f.submit "Submit" %></p>
<% end %>
What's the standard way for triggering the 'create' action with form_for?
Are you using the RESTful map.resources :searches ?
If so, shouldn't your :url be set to new_search_path ?
form_for is used with models. For a simple search form, I reccommend doing something like this:
<% form_tag posts_path, :method => :get do |f| %>
<%= f.text_field :query %>
<% end %>
You'll get /posts?query=wtf.

Resources