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|
Related
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 %>
I'm having troubles handling objects that not respect the validation.
I'm building an app in which an user can create a "Trip" model and then add steps to his trip, that I called "Traces". Each added trace prints a new part of a map present in the trip#show action.
The association between models is user has_many trips and trip has_many traces
In the users#show I put a "CREATE NEW TRIP" button linking to the trips#new and here I have the form_for with the field corresponding to the Trip attributes.
When I fill the form correctly everything is ok. When something is missing or wrong (for the validations) I get this error:
NoMethodError in Trips#create
undefined method `model_name' for Array:Class
------ in the trips_controller.rb
def create
#trip = current_user.trips.build(params[:trip])
if #trip.save
# handle a successful save
flash[:success] = 'Trip created!'
redirect_to user_trip_path(#trip, user_id: current_user.id)
else
#trip = []
#feed_items = []
render 'new'
end
end
------ in app/views/trip, in the new.html.erb
h1>Create a trip</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3 general-input">
<%= form_for ([current_user, #trip]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name,'Give a name to your trip ;)' %>
<%= f.text_field :name %>
<%= f.label :trip_start, 'Choose your starting point!' %>
<%= f.text_field :trip_start %>
<%= f.label :departure, 'When are you leaving?' %>
<%= f.date_select :departure, :start_year => Date.current.year %>
<%= f.label :arrive, 'And coming back home?' %>
<%= f.date_select :arrive, :start_year => Date.current.year %>
<%= f.submit 'Create a new trip', class: 'btn btn-primary btn-lg' %>
<% end %>
EDIT 1: problem solving removing #trace=[ ] from trips_controller.rb
EDIT 2:
I also have a similar problem with the creation of a new Trace:
The form for adding a new trace is in the trip#show page.
When I try to create a trace that not respects the validation (e.g. if I leave blank the "destination" field) I get this error:
NoMethodError in Posts#create
undefined method `any?' for nil:NilClass
When I'm on the Trip page where the form for the Traces is placed, the URL is like:
http://localhost:3000/users/2/trips/8
but when I create a not valide Trace it switchs to a path like:
http://localhost:3000/trips/8/posts
I suppose I'm doing something wrong handling the error messages. I probably misunderstood something, even because I'm new to Rails and web programming in general.
Here you are some code parts, hoping it helps to understand my mistake:
------ in the traces_controller.rb
def create
#trip= Trip.find(params[:trip_id])
#trace = #trip.traces.create(params[:trace])
if #trace.save
flash[:success] = 'Trace created!'
redirect_to user_trip_path(#trip, user_id: current_user.id)
else
#trace=[]
render 'trips/show'
end
end
------ in app/views/shared, in the add_trace_form.html.erb
<p>Complete your trip adding a route!</p>
<%= form_for ([#trip, #trip.traces.build]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="block post-form">
<div class="field ">
<%= f.text_field :end, placeholder: 'Where are you going next?' %>
<%= f.label :arr_day, 'When?' %>
<%= f.date_select :arr_day, :start_year => Date.current.year %>
<%= f.submit 'Add route', class: 'btn btn-primary btn-landing' %>
</div>
</div>
<% end %>
------ in app/views/shared, in the error_messages.html.erb
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |message| %>
<li>* <%= message %></li>
<% end %>
</ul>
</div>
<% end %>
------ in the routes.rb
resources :users do
resources :trips
end
resources :trips do
resources :traces
end
resources :traces
Thanks a lot
i think when you are passing the f.object in locales in render its is passing array not the active record object ,<%= render 'shared/error_messages', object: f.object %>.
Can u check inspecting object in your partial and what class it has.
Try inspecting object.errors.inspect
Try refering http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials
def update
if #note.update_attributes(note_params)
redirect_to :back, notice: "Note was updated."
else
render :edit
end
end
Is there a way to redirect back twice?
Here you go:
This is where the link for editing goes:
<p id="notice"><%= notice %></p>
<% url = "#{request.protocol}#{request.host_with_port}#{request.fullpath}" %>
<%= link_to 'Create New Page and Return Here', edit_page_path(1, :url => Base64.encode64(url) ) %>
<br>
After submit your url will be something like this:
http://localhost:3000/pages/1/edit?url=aHR0cDovL2xvY2FsaG9zdDozMDAwL2R1bW1pZXM%3D%0A
In the edit form:
I called it pages/_form.html.erb, Pass the URL as a hidden params.
<%= form_for(#page) do |f| %>
<% if #page.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#page.errors.count, "error") %> prohibited this page from being saved:</h2>
<ul>
<% #page.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :permalink %><br>
<%= f.text_field :permalink %>
</div>
<%= hidden_field_tag :url, params[:url].to_s %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
In controller that you have update method, in this case pages_controller.rb, simply Base64 it back and redirect the user:
def update
redirection = nil
if params[:url].present?
redirection = Base64.decode64(params[:url].to_s)
end
if #page.update(page_params)
if redirection.present?
path = redirection
else
path = #page
end
redirect_to path, notice: 'All Done.'
else
render :edit
end
end
Now user updates the form and redirected back to the first show or index page or any page that she is coming from.
Hope this help.
PS: You might want to clean it up a bit and pass the url from the controller, and put some checks on it. So you don't define any var at the view level. In the above code I just tried to solve this issue not really a design pattern oriented :)
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 want to display an "Edit" form in the "Show" view of a parent object.
My model looks like this:
A Trip has many Days which has many Activities.
Trip accepts nested attributes for Days. Days accepts nested Attributes for Activities.
When I am in the "Show" view for a Trip, how do I render an "Edit" form partial for an "Activity"?
I know I need to somehow specify to the Edit Form partial which Activity ID that I want to edit but I'm not sure how to pass along that information from the "Show" view of a "Trip".
<% #trip.days.each do |day| %>
<div id="daydiv_<%= day.id %>">
<b><%= day.summary %></b>
<%= content_tag_for :ol, day do %>
<% day.activities.each do |activity| %>
<li id="activity_<%= activity.id %>"><%= link_to activity.address, edit_activity_path(activity) %></li>
<% end %>
<% end %>
</div>
<% end %>
<div id="activity_form">
<%= render :partial => "/activities/form", :activity => #activity %>
</div>
my /activities/form partial looks like this:
<%= form_for(#activity) do |f| %>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This is what I ended up doing and it works.
In my show.html.erb for my "Trip".
show.html.erb
<div id="activity_form">
<h2>Activities</h2>
</div>
link to "Edit' an Activity. Notice the :remote => true which tells the Rails controller that this will be an AJAX request so to render edit.js.erb
<%= link_to activity.location[0, 20], edit_day_activity_path(day, activity), :class=>"btn btn-info fixedwidthbtn", method: :get, :remote => true
_form.html.erb This form partial is under the Activities View directory (../views/activities/_form.html.erb).
<%= form_for([#day, #activity], :remote => true) do |f| %>
<fieldset>
<%= f.label :title, "Activity" %>
<%= f.text_field :title, :rows => 1 %>
</fieldset>
<div class="actions">
<%= f.submit %>
</div>
</form>
<%= link_to 'Delete', [#day, #activity], method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
edit.js.erb This is a file under the Activities view directory (../views/activities/edit.js.erb). Says to grab the DOM element with ID of "activity_form" and render the partial "form"
$("#activity_form").html("<%= escape_javascript(render(:partial => "form"))%>");
update.js.erb I included this javascript after hitting update on the Edit form to render an updated partial of the Activities List. So that I don't have to reload the page to see an update.
$("#activities_list").html("<%= escape_javascript( render(:partial => "/trips/activities") ) %>");
routes.rb This is how I nest my routes. Only doing it 1 level following best practices.
resources :trips do
resources :days
end
resources :days do
resources :activities
end