Accessing nested model attributes inside a fields_for without using FormBuilder
You can call
f.object
to get to the object that the form is associated with.
Accessing nested model attributes inside a fields_for without using FormBuilder
You can call
f.object
to get to the object that the form is associated with.
How to omit existing child records in a nested form in rails?
You can use new_record?
method to distinguish between newly created record and old one:
<% form_for @user do |f| %>
<% f.fields_for :project do |project_form| %>
<%= render :partial => 'project', :locals => {:f => project_form} if project_form.object.new_record? %>
<% end %>
<%= add_child_link "New Project", f, :projects %>
<%= f.submit "save" %>
<% end %>
Creating a nested model form
One way to achieve the desired result is that you can craft a form that takes advantage of nested attributes support in Rails:
<%= form_for(@user) do |f| %>
<%= f.label :my_user_attribute %>
<%= f.text_field :my_user_attribute %>
<%= f.fields_for :profile do |fp| %>
<p>
<%= fp.label :my_profile_attribute %>
<%= fp.text_field :my_profile_attribute %>
</p>
<% end %>
<%= f.submit %>
<% end %>
You will also need to add the following to your User class:
accepts_nested_attributes_for :profile
You can read more about Active Record Nested Attributes here.
You can read more about the ActionView Form Helpers here (search down the page for "Nested Attributes Examples").
If you use this approach, along with good validations on both models, you won't have to worry about tracking the database IDs, because both will be created at the same time by ActiveRecord (but not until both model objects are valid).
Rails accepts_nested_attributes_for with f.fields_for and AJAX
It Is Possible
There's a very, very good tutorial on this here: http://pikender.in/2013/04/20/child-forms-using-fields_for-through-ajax-rails-way/
We also recently implemented this type of form on one of our development apps. If you goto & http://emailsystem.herokuapp.com, sign up (free) and click "New Message". The "Subscribers" part uses this technology
BTW we did this manually. Cocoon actually looks really good, and seems to use the same principles as us. There's also a RailsCast, but this only works for single additions (I think)
f.fields_for
The way you do it is to use a series of partials which dynamically build the fields you need. From your code, it looks like you have the fundamentals in place (the form is working), so now it's a case of building several components to handle the AJAX request:
- You need to handle the AJAX on the controller (route + controller action)
- You need to put your f.fields_for into partials (so they can be called with Ajax)
- You need to handle the
build
functionality in the model
Handling AJAX With The Controller
Firstly, you need to handle the Ajax requests in the controller
To do this, you need to add a new "endpoint" to the routes. This is ours:
resources :messages, :except => [:index, :destroy] do
collection do
get :add_subscriber
end
end
The controller action then translates into:
#app/controllers/messages_controller.rb
#Ajax Add Subscriber
def add_subscriber
@message = Message.build
render "add_subscriber", :layout => false
end
Add Your f.fields_for
Into Partials
To handle this, you need to put your f.fields_for
into partials. Here is the code form our form:
#app/views/resources/_message_subscriber_fields.html.erb
<%= f.fields_for :message_subscribers, :child_index => child_index do |subscriber| %>
<%= subscriber.collection_select(:subscriber_id, Subscriber.where(:user_id => current_user.id), :id, :name_with_email, include_blank: 'Subscribers') %>
<% end %>
#app/views/messages/add_subscriber.html.erb
<%= form_for @message, :url => messages_path, :authenticity_token => false do |f| %>
<%= render :partial => "resources/message_subscriber_fields", locals: {f: f, child_index: Time.now.to_i} %>
<% end %>
#app/views/messages/new.html.erb
<% child_index = Time.now.to_i %>
<div id="subscribers">
<div class="title">Subscribers</div>
<%= render :partial => "message_subscriber_fields", locals: {f: f, child_index: child_index } %>
</div>
Extend Your Build Functionality To Your Model
To keep things dry, we just created a build
function in the model, which we can call each time:
#Build
def self.build
message = self.new
message.message_subscribers.build
message
end
Child_Index
Your best friend here is child_index
If you're adding multiple fields, the big problem you'll have is incrementing the [id]
of the field (this was the flaw we found with Ryan Bates' tutorial)
The way the first tutorial I posted solved this was to just set the child_index
of the new fields with Time.now.to_i
. This sets a unique id, and because the actual ID of the new field is irrelevant, you'll be able to add as many fields as you like with it
JQuery
#Add Subscriber
$ ->
$(document).on "click", "#add_subscriber", (e) ->
e.preventDefault();
#Ajax
$.ajax
url: '/messages/add_subscriber'
success: (data) ->
el_to_add = $(data).html()
$('#subscribers').append(el_to_add)
error: (data) ->
alert "Sorry, There Was An Error!"
Rails many-to-many fields_for: How to access fields_for values?
The FormBuilder has attribute accessors like :object_name and :object.
For your particular issue try using:
<%= specform.object.name %>
Rendering nested form fields in Rails
I'm not sure if I've understood what you're asking but if you just want an access to the current item being automatically iterated via simple_fields_for
, then you can access it through the form builder as p.object
.
Related Topics
How to Dynamically Call Accessor Methods in Ruby
How to Fix Libv8 Error from Gemfile on Mavericks
Differences Between Literals and Constructors? ([] VS Array.New and {} VS Hash.New)
Gem Install Mongrel Fails with Ruby 1.9.1
Ruby - Array.Join Versus String Concatenation (Efficiency)
Ruby, Using Regex to Find Something in Between Two Strings
Building Hash by Grouping Array of Objects Based on a Property of the Items
Get Jekyll Configuration Inside Plugin
Ruby Net::Smtp - Send Email with Bcc: Recipients
Actiondispatch::Http::Uploadedfile.Content_Type Not Being Initialized in Rspec Test
Ruby Amazon S3 Access Denied When Listing Buckets
Breaking Ruby Module Across Several Files
Multiple Level Nested Layout in Rails 3
What Is the Point of Object#Presence in Rails