In my rails app, i have a project model which can have many project messages.
project has_many project_messages
In projects/show.html.erb, I would like to display all project messages for that project but also create new project messages from this view.
Currently, I am not able to create new project messages from this view.
I have reviewed a number of links but none work for me
Adding Form For Different Model In Same View,
Rails: Show form from different model in a view
My projects/show.html.erb file references as follows:
<div>
<% render partial: 'project_messages/form', :object => #project_message %>
</div>
In the projects_controller.rb file I have included the following:
def show
#project_message = ProjectMessage.new
end
&
def project_params
params.require(:project).permit(:title, :description, :phase_id, :RAGStatus, :currentpphase_id, :project_messages_attributes => [:pMessage, :user_id, :project_id])
end
And in the project.rb file I have the following code also:
accepts_nested_attributes_for :project_messages
project_messages/_form.html.erb
<%= form_with(model: project_message, local: true) do |form| %>
<% if project_message.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(project_message.errors.count, "error") %> prohibited this project_message from being saved:</h2>
<ul>
<% project_message.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form.label :pMessage %>
<%= form.text_field :pMessage, id: :project_message_pMessage, :class => 'au-input au-input--full au-input--h65', placeholder: 'Type a message' %>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
The project_messages form partial does not render so I cannot add a new message from this view.
Error:
undefined local variable or method `project_message' for #<#<Class:0x007f5455ced910>:0x007f5459ef8a30>
Did you mean? #project_message
When I add #project_message to the partial, it does not render.
Normally, when rendering a partial like:
<% render partial: 'form', object: #project_message %>
Because you are passing the #project_message as the specially-named object parameter, this creates a special variable with the name of the partial - in this case, form.
Therefore in the partial, you could reference this variable:
<%= form_with(model: form, local: true) do |form| %>
...However, in this case, it doesn't really make sense to call the local variable form! So I would be inclined to use whatever name you're passing it in as - for example, you could do:
<% render partial: 'form', project_message: #project_message %>
And then in the partial:
<%= form_with(model: project_message, local: true) do |form| %>
For more info, see: http://guides.rubyonrails.org/layouts_and_rendering.html#passing-local-variables
In project_messages/_form.html.erb, this line:
<%= form_with(model: project_message, local: true) do |form| %>
Should be changed to this:
<%= form_with(model: object, local: true) do |form| %>
You assigned a local variable object to a partial, so you should use it.
Also, partials may also be called this way:
<% render partial: 'form', object: #project_message %>
Related
I am trying to add a pre-launch sign-up email form on my pages index view. I made a form on my view, then created a controller, then made the model and table and ran the migration. Is this the right way to do it?
I am getting the following error:
NoMethodError in Pages#index
undefined method `premails_path' for #<#<Class:0x4fa8530>:0x52c1ec8>
on line:
<%= form_for(#premail) do |f| %>
from
<%= form_for(#premail) do |f| %>
<% if #premail.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#premail.errors.count, "error") %> prohibited this link from being saved:</h2>
My pages controller has the following:
class PagesController < ApplicationController
def index
#premail = Premail.new
end
end
My pages index view has the following:
<%= form_for(#premail) do |f| %>
<% if #premail.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#premail.errors.count, "error") %> prohibited this link from being saved:</h2>
<ul>
<% #premail.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :Email %><br />
<%= f.text_field :Email %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I generated the following model:
class Premail < ActiveRecord::Base
end
and the following db migrate table:
class CreatePremails < ActiveRecord::Migration
def change
create_table :premails do |t|
t.text :email
t.timestamps
end
end
end
What can I do differently to make this work?
Because you're loading this form on Pages#index, you'll benefit from using the url: argument like this:
<%= form_for #premail, url: pages_index_path do |f| %>
Update
From the Rails guide:
Resource-oriented style
In the examples just shown, although not indicated explicitly, we
still need to use the :url option in order to specify where the form
is going to be sent. However, further simplification is possible if
the record passed to form_for is a resource, i.e. it corresponds to a
set of RESTful routes, e.g. defined using the resources method in
config/routes.rb. In this case Rails will simply infer the appropriate
URL from the record itself. For example,
<%= form_for #post do |f| %> ... <% end %> is then equivalent to
something like:
<%= form_for #post, as: :post, url: post_path(#post), method: :patch,
html: { class: "edit_post", id: "edit_post_45" } do |f| %> ... <%
end %>
As you're using #premails instance var (which is an instance of the Premail class), your form_for helper will be trying to use a premails path. To use a pages path (as per your question), you'll need to manually set the url option
So, for example, case from http://guides.rubyonrails.org/getting_started.html As you can see, if you try to create invalid post, you will see error messages:
<%= form_for #post do |f| %>
<% if #post.errors.any? %>
<div id="errorExplanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
How to implement error messages rendering for associated Comment model, keeping in mind that comment creation form is placed in posts/show view?
Form code is usually kept in the folder of the matching model in a _form.html.erb partial that is rendered in both new.html.erb and edit.html.erb (to see a good example, generate a scaffold for a sample model).
What you can do in your case is render this comments form partial in the posts show action.
app/views/posts/show.html.erb
<%= render 'comments/form', comment: #comment || #post.comments.build # Whatever you have here %>
app/views/comments/_form.html.erb
<%= form_for comment do |f| %>
<%= render 'error_messages', target: comment %>
...
<% end %>
In addition, showing error messages usually is the same in all forms, so in order to remove duplication, you can extract this code into a seperate partial.
app/views/application/error_messages.html.slim # here is slim syntax, convert as nescessary
/ error explanation
/
/ = render 'shared/error_explanation', target: #school
/
- if target.errors.any?
.error-messages
h4 Please correct the following fields:
ul
- target.errors.full_messages.each do |message|
li = message
Hope this helps.
I have a links model which has all the generic scaffold created for it, however, rather than go to the link#new page, I'd like to submit a form from my homepage that populates a new record.
I only have one text field, but im not sure how to construct the form. I read somewhere you have to specify the controller in the form field but this doesn't appear to be working.
<%= form_for(:link, #link) do |f| %>
<% if #link.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#link.errors.count, "error") %> prohibited this link from being saved:</h2>
<ul>
<% #link.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :url %><br />
<%= f.text_field :url %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You don't need to specify anything if you are using default routes.
If the #link is an object that doesn't exist in database, Rails will automatically think this is a form for #new. So the form action will be /links, and method is post, which is the default resource to #create
In your case, you don't need to do anything, just revise the form code to:
<%= form_for(#link) do |f| %>
....
Besides, you need to prepare #link object in home controller, something like
#link = Link.new
All you have to do is add a url parameter to the form_for helper
<%= form_for :link, url: your_home_path do |f| %>
I have two models generated with generate scaffolding, one is a LogBook the other is LogEntry. I want to render the form partial for LogEntry on the LogBook show page. When I call render on the partial I get this error:
undefined method `model_name' for NilClass:Class
I assume it is because the default _form uses an instance variable which isn't present when called from a separate controller. So I tried converting the LogEntry _form.html.erb to use local variables and passed them in via the render call. After this here is the error:
Model LogEntry does not respond to Text
How can I include this partial into the show page form a different controller?
Models:
class LogBook < ActiveRecord::Base
belongs_to :User
has_many :LogEntries, :dependent => :destroy
end
class LogEntry < ActiveRecord::Base
belongs_to :LogBook, :class_name => "log_book", :foreign_key => "log_book_id"
end
LogEntry _form.html.erb (using local variable):
<%= form_for(log_entry) do |f| %>
<% if log_entry.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(log_entry.errors.count, "error") %> prohibited this log_entry from being saved:</h2>
<ul>
<% log_entry.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :Text %><br />
<%= f.text_field :Text %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
LogBook show.html.erb:
<p id="notice"><%= notice %></p>
<p>
<b>Name:</b>
<%= #log_book.name %>
</p>
<%= render 'log_entries/form', :log_entry => #log_book.LogEntries.new %>
<%= link_to 'Edit', edit_log_book_path(#log_book) %> |
<%= link_to 'Back', log_books_path %>
You can render whatever partial you want as long as you give it's path from the view folder:
<%= render :partial => '/log_entries/form', :log_entry => #log_book.log_entries.build %>
Your path must begin with a / to let Rails
know you're relative to the view folder.
Otherwise it's assumed to be relative to your current folder.
As a sidenote, it's good practive to avoid using instance variables in partial, you did it right then.
Just seen you have an error in your partial's form:
:Text
Should not be a valid column name of your model. Try :text
Try switching the render method as follows:
<%= render :partial => 'log_entries/form', :log_entry => #log_book.LogEntries.new %>
Using just render works when passing an instance variable of the object. However, since you're specifying a file, it's best to use the option.
I have a _form.html.erb partial that comes from the standard rails 3 template for the model project.
#view:
<div id="content">
<%= link_to 'Edit', edit_project_path(#project), :remote => :true %>
</div>
#projects controllers
def edit
#project = Project.find(params[:id])
respond_to do |format|
format.html # edit.html.erb
format.js { render "form.js.rjs" }
end
end
#form.js.rjs
page.replace_html "content", :partial => 'form'
#_form.html.erb
<%= form_for(#project) do |f| %>
<% if #project.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#project.errors.count, "error") %> prohibited this project from being saved:</h2>
<ul>
<% #project.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :name %>
<%= f.text_field :name %>
</p>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
While i click that link to edit a project, the form does come through through ajax, but the method for the form is now post instead of put. which means as i submit, it will create new projects with the same attributes as the existing one that I am editing b/c of the create method that's called upon receiving a post request.
I know that form_for(#project) rely on record id to tell whether it's new or not, I looked through all the sources for form_for, form_tag, extras_tags_for_form, form_tag_html but cannot find a place where they specify which method for the form tag is to be used. The closest place where it defines the method is in extra_tags_for_form, but in that method, it is merely sieving through the :method option hash that's already passed to it, but from where is this :method option passed? I cannot find.
any ideas?
You are on the right track, form_form(obj) will look at the objects dirty flag to figure out what to do with it. That doesn't mean you can't tell it what to do though :)
form_for actually has a few hashes in its optional params, :method lives inside :html. so to force it to be put, just do something like this
form_for #project, :html => {:method => :put} do |f|