Does rails automatically pass '#variables' into partial? - ruby-on-rails

I have a simple setup:
class EventsController < ApplicationController
def edit
#user = User.find(params[:user_id])
#event = Event.find(params[:id])
end
end
events\edit.html.erb:
<h1>Edit <%= #user.name %>'s event</h1>
<%= render 'form' %>
events\_form.html.erb:
<%= form_for [#user, #event] do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.submit %>
<% end %>
To my biggest surprise this code is working and I am not getting any errors. Form partial knows about #user and #event!
I always thought I have to pass parameters as locals in order to access them in the partial, so the render from the view have to be:
<%= render 'form', user: #user, event: #event %>
And form_for line should be changed to:
<%= form_for [user, event] do |f| %>
Am I missing something here? Is it one of those days when I confused my self so much that I should probably get some sleep?
I am running this on Rails 4.1.4 and events are nested resources of user if that changes anything.

Your parameter is an instance variable. As such it is available to any partials rendered in the view.
You can check out more about rendering on the rails guides: http://guides.rubyonrails.org/getting_started.html#rendering-a-partial-form
It's good practice to pass in variables to partials as locals, as its easier to reuse the partial in other actions:
http://guides.rubyonrails.org/layouts_and_rendering.html#passing-local-variables
Also if you try to access a local variable you didn't pass into the partial, your view with explode, while an instance variable will just be nil. Having the view explode is, in my opinion, easier to debug.

Related

How to get Rails form_for to point towards different controller

I have a form for creating new comments. This code exists in a page that is under a different controller (let's say it's app/views/posts/show.html.erb).
<%= form_for Comment.new do |f| %>
<%= f.label :content %>
<%= f.text_field :content %><br/>
<%= f.submit %>
<% end %>
The form works if I have Comment.new like above, but I want to use an instance variable like form_for #comment, similar to the first code snippet in this link: https://api.rubyonrails.org/v5.2.3/classes/ActionView/Helpers/FormHelper.html
In order to do so, I thought I need to define a new function like this and assign an empty comment. I tried putting this code in both the posts_controller and comments_controller.
def new
#comment = Comment.new
end
But when I replace Comment.new with #comment, I get this error: ActionView::Template::Error (First argument in form cannot contain nil or be empty):
This leads me to believe that neither of the new methods are being called. What am I doing wrong here?
My routes.rb looks like this:
Rails.application.routes.draw do
root to: 'posts#show'
resources :messages
end
if you are using show page (app/views/posts/show.html.erb) to display form
add this line in the show action of posts controller
# posts_controller
def show
#comment = Comment.new
end
and if you also want to submit your form other than the comment's create action mention the url in form_for tag
<%= form_for #comment, url: posts_path do |f| %>
<%= f.label :content %>
<%= f.text_field :content %><br/>
<%= f.submit %>
<% end %>

form_for Ruby On Rails

I'm trying to follow a tutorial on using basic AJAX to add a record to a list in place, and I'm having issues using form form_for.
Here is my code.
<%= form_for ([#product, #product.new]) do |p| %>
<p>
<%= p.label :product_part %>
<%= p.text_field :product_part%>
</p>
<p>
<%= p.submit %>
</p>
<% end %>
The error I am getting is
undefined method `new' for nil:NilClass
I understand why I am getting the error (#products hasn't been "initialized") but I have no idea how to fix this issue (I am sure it's simple). I have seen something about putting a resource in the routes file, but I do not know for sure.
If you're trying to make a form for a new product, you should (in your controller) be setting #product to an instance of a new Product:
# app/controllers/products_controller.rb
def new
#product = Product.new
end
In your view, using [#product, #product.new] makes no sense. You can't invoke new on an instance of a product. It's very unclear why you're not simply using the following, which is the correct use of form_for with a new instance of an ActiveRecord model:
form_for #product do |p|
Do this:
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
def new
#product = Product.new
render :form
end
def edit
#product = Product.find params[:id]
render :form
end
end
#app/views/products/form.html.erb
<%= form_for #product, remote: true do |f| %>
<%= p.label :product_part %>
<%= p.text_field :product_part%>
<%= f.submit %>
<% end %>
This will do everything you need for both the new and edit methods (which you raised concerns about in your comments with #meagar).
This should be corroborated with the following routes (you can see why here):
#config/routes.rb
resources :products
I would say In case you need to look the form_for helper ; to understand the behavior of the method.
The method form_for It accept the argument as record, options = {}. The value of record could be a symbol object or a newly object of respective class in your case Person.new.
Second argument could be
:url, :namespace, :method, :authenticity_token, :remote , :enforce_utf8, :html
Among them :remote => true option is used as the Ajaxify your request.
form_for is a helper that assists with writing forms. form_for takes a :remote option. It works like this:
<%= form_for(#article, remote: true) do |f| %>
....
<% end %>
This will generate the following HTML:
<form accept-charset="UTF-8" action="/articles" class="new_article" data-remote="true" id="new_article" method="post">
...
</form>
Note the data-remote="true". Now, the form will be submitted by Ajax rather than by the browser's normal submit mechanism.
For more info about Form-For helper
Hope this solve your problem!!!.

Rails datetime_local_field help not pre filling

I have a Job model with a first_booking_time attribute. The datetime_local_field form helper doesn't pre fill the attribute stored on the Job instance.
# renders empty field even though job.first_booking_time is set
<%= form_for #job do |f| %>
<%= f.datetime_local_field :first_booking_time %>
<% end %>
However, if I use the datetime_local_field_tag helper and pass in the value, it works:
# pre fills what's in job.first_booking_time
<%= form_for #job do |f| %>
<%= datetime_local_field_tag 'job[first_booking_time]', #job.first_booking_time %>
<% end %>
How can I use the first syntax with the helper pre filling?
Try the below code
<%= f.datetime_local_field :first_booking_time , :value => #job.first_booking_time %>

Rails 4.2 form error handling outside controller default view

I'm following the official starter guide from rails guides. It's a great tutorial but I'm having doubts about how to handle form errors display for the comments form inside the article view.
<%= form_for([#article, #article.comments.build]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
<%= f.submit %>
</p>
I don't know if this is something so simple to see that the guides don't bother to explain about it or is something more complex that it looks like.
What is the best process to display form errors after submission in a outside view in rails 4.2?
Also, as an aside note. What is the form workflow between different views and controllers?
Thanks in advance.
In your create action you can do:
def create
#article = Article.find(params[:article_id])
#comment = #article.comments.build(params[:comment])
#comment.user = current_user
if #comment.save
redirect_to article_path(#article)
else
render 'new', locals: { errors: #comment.errors.full_messages }
end
end
The idea is that in your create action you try to save a new record. If everything is fine, the user will be redirected to the #show action of the article. Otherwise, Rails will render again the 'new' template. This time errors will be passed to your view. So you should add in your view validation for errors object and iterate over them (it's array of Strings) to display all messages in case that there are error messages.

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

Resources