I have an edit form that is not a devise form (i have a devise edit on a different view) to edit a users details. However the form inputs only appear if the data is already there. So a user is unable to add new details to the form, as the inputs don't appear at all.
Is this happening as i'm not using a devise form view?
This is the code in my own edit file:
<%= render "devise/registrations/details", f: f, resource: #resource, addressable: #resource, default_location: nil %>
Then in the devise/registration/details i have this:
<%= f.simple_fields_for :address do |fields| %>
<%= render "address/fields", fields: fields, addressable: addressable, resource: resource %>
<% end %>
However, i think the inputs are not showing up as fields are blank in the iteration. However these fields are showing up in the actual devise/edit file, even if they are blank, just not in my new one.
When using fields_for and simple_fields_for (which is basically just a pimped up version of the former) you have to "seed" the inputs in order for them to appear.
Consider this simplified plain rails example:
class UsersController < ApplicationController
def new
#user = User.new
#user.build_address
end
# ...
end
<%= simple_form_for(#user) do |f| %>
<%= f.simple_fields_for(:address) do |af| %>
<%= af.input :street %>
<% end %>
<% end %>
If we remove the line #user.build_address there will be no nested inputs. Thats because simple_fields_for calls the method #address on #user and creates inputs for the record. If the association is one or many to many it would iterate through the association. If the method returns nil or empty there are no inputs to create - it would be like calling form_for with nil.
You can also pass a second argument to fields_for to manually specify the record object:
<%= simple_form_for(#user) do |f| %>
<%= f.simple_fields_for(:address, #user.address || #user.build_address) do |af| %>
<%= af.input :street %>
<% end %>
<% end %>
In Devise you can do this by passing a block to super or by overriding #build_resource.
class MyRegistationsController < Devise::RegistrationsController
def new
super { |resource| resource.build_address }
end
end
Related
the following rails controller action, creates an array of records to be created:
#guests = []
#quantity.times do
#guests << Guest.new
end
Then the form invokes the array of records to be created in the following manner
<%= form_tag guests_path do %>
<% #guests.each do |guest| %>
<%= fields_for 'guests[]', guest do |f| %>
The goal is to render some fields only for the first of these records/
How can the index value of the first guest be invoked (various attempts such as if #guests[0] generate errors.
I think what you are looking for is each with index
<%= form_tag guests_path do %>
<% #guests.each_with_index do |guest,index| %>
# Do something with index
<%= fields_for 'guests[]', guest do |f| %>
I am trying to write a form for an array
<%= form_for #user, html: {multipart:true} do |f| %>
<%= render "shared/error_messages", object: f.object %>
<label for="user-amenities" class="top">Amenities</label>
<ul class="group" id="user-amenities">
<% User.amenities_list.each_with_index do |amenity, index| %>
<li class="checkbox-li">
<input type="checkbox" name="user_amenities_indicies[]" value="<%= index %>">
<%= amenity %>
</input>
</li>
</ul>
<% end %>
However I am not utilizing the |f| and it is not saving the options in the amenities_indices. Any idea on how to refactor this code to utilize the f so the user information can be saved?
Try simple_form https://github.com/plataformatec/simple_form/blob/master/README.md
What you're looking for is :collection and then :as
Code Block
The refactoring you seek is basically that you need to use the f. with all your inputs
Reason being that if you call form_for, it essentially means you're calling a huge code block, in which every attribute needs to be bound to your form object, in order to render with your params hash correctly
In short - your form_for renders an HTML form, keeping the names of the <input> elements in line with the requirement of your application to load the params. The problem you have is that omitting the f. call will keep those inputs outside the scope for your params, leading to the issue you're seeing.
--
Save
If you don't have any associative data (which I've described below), you'll want to include your inputs in the f. code block:
<%= form_for #user do |f| %>
<% User.amenities_list.each_with_index do |amenity, index| %>
<%= f.check_box :user_amenities_indicies, index %>
<% end %>
<% end %>
This will pass the checked values of the user_amenities_indicies checkboxes through to your controller. This should work, and is the correct syntax for you
--
fields_for
A further addition is that I don't know whether you're trying to populate associative data or not here - but if you were trying to create data for another model, you'll want to use fields_for:
#app/controllers/users_controller.rb
Class UsersController < ApplicationController
def new
#user = User.new
#user.user_amenities.build
end
def create
#user = User.new(user_params)
#user.save
end
private
def user_params
params.require(:user).permit(:user, :params, user_amenities_attributes: [])
end
end
This will allow you to create a form using fields_for, like this:
<%= form_for #user do |f| %>
<%= f.fields_for :user_amenities do |ua| %>
# fields for user_amenities
<% end %>
<% end %>
I have created a simple form to create an instance of a modle and for some reason it is not calling the create method in the controller. Here is the form code:
<% #house.mates.each do |mate| %>
<p><%= mate.name %></p>
<% end %>
<h2>Add a new mate:</h2>
<%= form_for #mate do |f| %>
<p><%= f.label "Name" %>
<%= f.text_field :name %>
<%= f.hidden_field :house_id %>
</p>
<%= f.submit "Submit", :action => :create %>
<% end %>
Here is the controller code:
class MatesController < ApplicationController
def new
#mate = Mate.new
end
def create
#mate = Mate.new(params[:mate])
#mate.save
redirect_to house_path(current_house)
end
end
There is a many to one relationship between the Mate model and the House model... I am fairly new to rails but I have made other apps with similar forms, and I have never had this problem before. I can create and save Mate objects in the console, and I am not getting any errors, so it seem that somehow the controller method is not being called. Any help is much appreciated!
In fact, if other things have no problem, your #mate object should be created. You just can't see it in house page because you have not associated #mate with house in your code.
In your form you referred :house_id, but this attribute is nil when you rendering the form.
The reason is you have not assigned it in controller.
In controller you need to initialize #mate from house object to have house_id inside it
def new
#house = something
#mate = #house.mates.new # Instead of Mate.new
end
My question is: why doesn't .becomes pass errors over to the new object? Isn't this the expected behaviour?
I have the following single table inheritance classes in a rails app:
class Document < ActiveRecord::Base
validates :title, :presence => true
end
class LegalDocument < Document
end
class MarketingDocument < Document
end
I want to use the same controller and set of views to edit both LegalDocuments and MarketingDocuments, so I am using DocumentsController < ApplicationController with the following edit and update actions:
def edit
#document = Document.find(params[:id])
end
def update
#document = Document.find(params[:id])
if #document.update_attributes(params[:document])
redirect_to documents_path, :notice => "#{t(:Document)} was successfully updated."
else
render :action => "edit"
end
end
and the following in my edit view:
<%= form_for #document.becomes(Document) do |f| %>
<% if f.object.errors.present? %>
<div class="error_message">
<h4><%= pluralize(f.object.errors.count, 'error') %> occurred</h4>
</div>
<% end %>
<div>
<%= f.label :title %>
<%= f.text_field :title, :class => "inputText" %>
</div>
<%= f.submit %>
<% end %>
If title is filled in, the documents update correctly.
If title is left blank, I am returned to the edit view BUT the error is not shown.
From debugging, I know it's not showing because f.object.errors is nil. However, from debugging, I also know #document.errors is NOT nil, as expected.
My question is: why doesn't .becomes pass errors over to the new object? Isn't this the expected behaviour?
Yes, I noticed that too.
Just change f.object.errors.present? by #document.errors.any? ( or #document.errors.present?).
If you really want to use f.object.errors.present?, write becomes in the controller (both edit and update actions) instead of in the view:
def edit
#document = Document.find(params[:id]).becomes(Document)
end
def update
#document = Document.find(params[:id]).becomes(Document)
# ....
end
And then in the view:
<%= form_for #document do |f| %>
<% if f.object.errors.present? %>
<p>Errrorsss....</p>
<% end %>
#.....
It happens because the url of the form is build according to #document.becomes(Document) (=> PUT document/:id) but #document is created according to its "true" class (a subclass of Document).
If you have pry (highly recommended), write:
def update
#document = Document.find(params[:id])
binding.pry
# ...
end
And then inspect #document. You will see that #document is an instance of LegalDocument or the other subclass even though you called #document.becomes(Document) in your form.
So in final f.object and #document are not the same.
This explains why you can't see f.object.errors when validation fails.
Edit
The 'best way' to deal with STI and form is NOT to use becomes:
<= form_for #document, url: { controller: 'documents', action: 'update' }, as: :document do |f| %>
<% if #document.errors.any? %>
# or if f.object.errors.any?
# handle validation errors
<% end %>
# your form...
<% end %>
This enables you:
to have only one controller (documents_controller)
to have only one resource (resources :documents)
it keeps trace of your subclasses: a LegalDocument will be store as a LegalDocument. No conversion: You don't have to store its class before the conversion to Document and then reassign it later.
Plus, your subclass is available in your form, so you can (let's imagine) build a select for the type.
your controller looks cleaner: #document = Document.find params[:id] nothing more. Just like a classic resource.
If you want to share this form across different actions(typically edit and new):
<%= form_for #document, url: { controller: 'media_files', action: action }, as: :media_file do |f| %>%>
# edit.html.erb
<%= render 'form', action: 'update' %>
# new.html.erb
<%= render 'form', action: 'create' %>
Pretty much it is a bug and it should work as you initially expected. The following patch to address the issue looks like it was pulled back in October
https://github.com/lazyatom/rails/commit/73cb0f98289923c8fa0287bf1cc8857664078d43
I have a parent model that accepts child attributes.
class User < ActiveRecord::Base
accepts_nested_attributes_for :spec
attr_accessible :name, :spec_attributes
In the view I have a form that gets information for 3 models. I use a generic form_tag.
<% form_tag(action) do %>
.
.
.
<% fields_for "user[spec_attributes]" do |spec_form|%>
<%= spec_form.check_box :alert_greeting %>
<%= spec_form.label :alert_greeting, "Email me when new greetings are posted" %>
<% end %>
<% end %>
In the Controller
#user = User.find(session[:user_id])
if #user.update_attributes(params[:user])
do something.
end
The database is getting updated and the all seems to be working.
However when I go back to the form to edit again, even though the value for the checkbox is showing 1 the check box is not checked.
Any ideas as to how to show the checkbox as being checked when it is supposed to be?
Thanks a lot in advance.
You need to reference the specific spec record that is within the user you're calling. Try changing
<% fields_for "user[spec_attributes]" do |spec_form|%>
to
<% fields_for #user.spec do |spec_form|%>
You'll need to make sure that you have a non-nil spec object built for the user (but not necessarily saved) in your edit controller action.
You can do attributes and nested attributes using two fields_for calls like this:
<%= form_tag(action) do %>
... other form tags ...
<%= fields_for :user, #user do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.fields_for :spec do |s| %>
<%= s.check_box :alert_greeting %>
<%= s.label :alert_greeting, "Email me when new greetings are posted" %>
<% end %>
<% end %>
<% end %>