Rails nested form edits - ruby-on-rails

I am trying to figure out the whole nested form thing, and am having issues rending the form for editing.
I'm not doing this the "typical way" with a new and create action, because i'm not actually going to be saving anything. I'm just using this form to generate some tests.
Here is what I have..
model.rb
class Model < ActiveRecord::Base
has_many :columns
has_many :associations
accepts_nested_attributes_for :associations, :reject_if => lambda { |a| a[:type].blank? }
accepts_nested_attributes_for :columns, :reject_if => lambda { |a| a[:name].blank? }
attr_accessible :name, :associations_attributes, :columns_attributes
end
column.rb
class Column < ActiveRecord::Base
belongs_to :model
attr_accessible :db_index, :mass_assign, :max_length, :min_length, :name, :required, :unique, :columns_attributes
validates :name, :presence => true
end
generator_controller.rb
def model
if params['submit']
#model = Model.new(params[:model])
#model.columns.build
else
#model = Model.new
3.times { #model.columns.build }
end
render 'generator/model'
end
form view
<%= nested_form_for(#model, :url => '/model', :html => {:style => 'width:100%;'}) do |f| %>
<%= flash_helper() %>
<h3>Model Name</h3>
<%= f.text_field :name %>
<h3>Add Table Columns</h3>
<div id="columns">
<% f.fields_for :columns do |builder| %>
<div class="float_left column">
<%= builder.label :name %>
<br>
<%= builder.text_field :name, :size => 20 %>
</div>
<div class="float_left column">
<%= builder.label :mass_assign %>
<br>
<%= builder.check_box :mass_assign %>
</div>
<div class="float_left column">
<%= builder.label :required %>
<br>
<%= builder.check_box :required %>
</div>
<div class="float_left column">
<%= builder.label :unique %>
<br>
<%= builder.check_box :unique %>
</div>
<div class="float_left column">
<%= builder.label :db_index %>
<br>
<%= builder.check_box :db_index %>
</div>
<div class="float_left column">
<br>
<%= image_tag "delete-icon24x24.png", :class => "btnDel clickable" %>
</div>
<br class="clear_float">
<% end %>
<%= f.link_to_add image_tag('add-icon.png'), :columns %>
</div>
<br><br>
<input type="submit" name="submit">
<p>
<%= #results %>
</p>
<% end %>
So the problem is, the form when new is not displaying the columns that "3.times { #model.columns.build }" is supposed to do. Also, when I submit the form, I need to figure out why it is getting rid of all of those nested attributes that were added. So each time I submit the form, and it renders the form again, all the attributes are gone. (The "model" attributes are still there, but the "column" attributes reset each time)
Any ideas on what i'm doing wrong?
Thanks

I am embarrassed to even admit this, but hopefully it will help someone else with one more thing to check if they have the same problem.
<% f.fields_for :columns do |builder| %>
changed to:
<%= f.fields_for :columns do |builder| %>
Yeah.. can't believe I did that...

Related

Rails, fields_for nested attributes - Avoid displaying existing objects on edit

I am trying to have my Edit form only allow for the creation of new elements. Currently it automatically displays all of the existing elements. Below is my current code. I have a standard has_many, belongs_to relationship between Orders and Documents. I only want the ability for users to add new documents and never edit the existing documents. With the current code It displays all associated Documents on the edit form.
<%= f.fields_for :documents, child_index: Time.now.to_i do |document| %>
<div class="col-md-5">
<%= document.label :doc_type, "Type", :style => "color:red;" %>
<%= document.collection_select :doc_type_id, DocType.order(:name), :id, :name, { prompt: true, class: "form-full"} %>
<br>
</div>
<div class="col-md-7">
<%= document.label :description,"Description **", :style => "color:red;" %>
<%= document.text_field :description, style:"color:black;" %>
<br>
</div>
<div class="col-md-12">
<br>
<%= document.file_field :doc, style:"color:white;" %>
</div>
<div class="col-md-12" style="background:#272727;">
<hr class="style2">
</div>
<% end %>
class Order < ActiveRecord::Base
belongs_to :user
has_many :properties, :dependent => :destroy
has_many :documents, :dependent => :destroy
accepts_nested_attributes_for :documents,
:allow_destroy => true,
:reject_if => :all_blank
accepts_nested_attributes_for :properties,
reject_if: proc { |att| att["city"].blank? }
end
class Document < ActiveRecord::Base
belongs_to :order
belongs_to :doc_type
end
With The below code I was able to have the form appear correctly (not showing existing Documents) but when adding a new Document via the Edit form, the Document is never actually created.
<% if !current_page?(action: 'edit') %>
<%= f.fields_for :documents, child_index: Time.now.to_i do |document| %>
<div class="col-md-5">
<%= document.label :doc_type, "Type", :style => "color:red;" %>
<%= document.collection_select :doc_type_id, DocType.order(:name), :id, :name, { prompt: true, class: "form-full"} %>
<br>
</div>
<div class="col-md-7">
<%= document.label :description,"Description **", :style => "color:red;" %>
<%= document.text_field :description, style:"color:black;" %>
<br>
</div>
<div class="col-md-12">
<br>
<%= document.file_field :doc, style:"color:white;" %>
</div>
<div class="col-md-12" style="background:#272727;">
<hr class="style2">
</div>
<% end %>
<% else %>
<%= f.fields_for #order.documents.build, child_index: Time.now.to_i do |document| %>
<div class="col-md-5">
<%= document.label :doc_type, "Type", :style => "color:red;" %>
<%= document.collection_select :doc_type_id, DocType.order(:name), :id, :name, { prompt: true, class: "form-full"} %>
<br>
</div>
<div class="col-md-7">
<%= document.label :description,"Description **", :style => "color:red;" %>
<%= document.text_field :description, style:"color:black;" %>
<br>
</div>
<div class="col-md-12">
<br>
<%= document.file_field :doc, style:"color:white;" %>
</div>
<div class="col-md-12" style="background:#272727;">
<hr class="style2">
</div>
<% end %>
<% end %>
Any help/advice/resources would be appreciated. Been stuck on this for a bit. Also I have not refactored into partials yet so please forgive the messiness.
THANKS!!
-Vinny

Cocoon First Option Not Saving - Nested Fields

I'm using Cocoon for a nested form, however the first set of fields do not save into the database, if I create a second row they seem to save just fine?
I'm guessing its just something I'm overlooking
class Form < ActiveRecord::Base
has_many :questions
accepts_nested_attributes_for :questions, :reject_if => :all_blank, :allow_destroy => true
end
class Question < ActiveRecord::Base
belongs_to :form
end
----- form_controller.rb
def new
#form = Form.new
#form.questions.build
end
def create
#form = Form.new(form_params)
if #form.save
redirect_to action: "show", id: #form.id
else
render('new')
end
end
def form_params
params.require(:form).permit(:title, :description, :status, questions_attributes: [:form_id, :question_type, :question_text, :position, :answer_options, :validation_rules, :done, :_destroy])
end
<%= simple_form_for Form.new ,:url => {:action => :create} do |f| %>
<div class="section-row">
<div class="section-cell">
<%= f.label :title, "Form Title" %>
<%= f.text_field :title, placeholder: "Form Title" %>
<br/></br>
<%= f.label :description, "Form Description" %>
<%= f.text_area :description, placeholder: "Form Description" %>
<%= f.hidden_field :status, value: "online" %>
</div>
</div>
<div class="section-row">
<div class="section-cell">
<div id="questions">
<%= simple_fields_for :questions do |question| %>
<%= render 'question_fields', :f => question %>
<%= link_to_add_association 'add question', f, :questions %>
<% end %>
</div>
</div>
</div>
<div class="section-row">
<div class="section-cell">
<%= f.submit "Create Ticket", class: "btn btn-primary btn-lg" %>
</div>
</div>
<% end %>
---- _question_fields.html.erb
<div class='nested-fields'>
<%= f.label :question_type, "Question Type" %>
<%= f.select :question_type,
options_for_select([['Text Box','textbox'],['Dropdown Options','select'], ['Mutiple Choice','radio']], params[:question_type]),
{}, { :class => '' } %>
</div>
You forgot the f in front of the simple_fields_for : then the simple_fields_for has no knowledge of the form, nor the associations, so it will name the parameters posted to the controller differently, so it will be blocked by your strong parameters definition.
So if you write
f.simple_fields_for :questions do |question|
it should just work ;)
Smaller remarks:
the link_to_add_association should be outside of the loop, otherwise it will not be visible if there are none, and will be displayed for each question (which may or may not make sense in your UI).
you should add the id inside the strong parameters for questions_attributes (important for editing/deleting)
You wrote the link_to_add_association inside the simple_fields_for
you should write your form as follows (as documented):
<div id="questions">
<%= f.simple_fields_for :questions do |question| %>
<%= render 'question_fields', f: => question %>
<% end %>
<div class="links">
<%= link_to_add_association 'add task', f, :questions %>
</div>

Nested attributes (Paperclip) validation

I'm working on a nested form where a user when creating a Screen should also attached an image (a separate model called Screenshot). I'm trying to validate the presence of the attachment before saving the new Screen to the db. I tried to validate the presence of the attachment in the Screenshot model, but that only precent Screenshot to be saved, while it still creates a Screen.
Here's my models:
class Screen < ActiveRecord::Base
belongs_to :project
has_many :screenshots
validates :name, presence: true
accepts_nested_attributes_for :screenshots
validates_presence_of :screenshots
end
class Screenshot < ActiveRecord::Base
belongs_to :screen
has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }
end
here's my controller:
def create
#screen = Screen.create(screen_params)
if #screen.save
flash[:notice] = "A new screen has been added to this project"
redirect_to [#screen.project]
else
render :action => 'new'
end
end
And lastly here's my form:
<%= form_for ([#project, #screen]), :html => { :multipart => true } do |f| %>
<% if #screen.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#screen.errors.count, "error") %> prohibited this screen from being saved:</h2>
<ul>
<% #screen.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.hidden_field :project_id %>
</div>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<%= f.fields_for :screenshots, #screen.screenshots.build do |s| %>
<%= s.hidden_field :screen_id, :value => #screen.id %>
<%= s.hidden_field :version, :value => "1" %>
<%= s.label :image %><br>
<%= s.file_field :image %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Any help to this noob is greatly appreciated.
Several things for you:
Form
You shouldn't build new objects in the form itself. That needs to happen in the controller's new function
<%= f.fields_for :screenshots do |s| %>
<%= s.hidden_field :screen_id, :value => #screen.id %>
<%= s.hidden_field :version, :value => "1" %>
<%= s.label :image %><br>
<%= s.file_field :image %>
<% end %>
You can declare them like this:
def new
#screen = Screen.new
#screen.screenshots.build
#any more build declarations go here
end
Verification
I believe you should put the verification into the screnshots model, as the nested parameters are passed directly to the corresponding model. I'm not sure on that though.
In the screenshots model, I would put the validates_presence_of :screenshots, with the name as your Paperclip attachment's name

Rails - :_destroy method not working

I am trying to use :_destroy method in a nested form, but it just does not work
There are two models:
class Setting < ActiveRecord::Base
attr_accessible :category, :name, :setting_items_attributes, :_destroy
attr_accessor :_destroy
has_many :setting_items, :dependent => :destroy
accepts_nested_attributes_for :setting_items, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
end
class SettingItem < ActiveRecord::Base
attr_accessible :setting_id, :value
belongs_to :setting
end
In the controller I create a instance:
def edit
#setting = Setting.find(params[:id])
#setting.setting_items.build
end
And the form looks like this:
<%= form_for(#setting) do |f| %>
<div class="field">
<%= f.label :category %>
<%= f.text_field :category %>
</div>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<hr>
<h3>Params:</h3>
<%= f.fields_for :setting_items do |s| %>
<span>
<div class="fields">
<%= s.text_field :value %>
<%= s.hidden_field :_destroy %>
<%= link_to_function "delete", "remove_fields(this)"%>
</div>
<% end %>
<div class="actions">
<%= f.submit "Update", :class => "btn btn-primary"%>
</div>
<% end %>
Also the function I use is here:
function remove_fields(link){
$(link).prev("input[type=hidden]").val("1");
$(link).parent().fadeOut("slow");
}
So the setting_items form is simply not working at all, It shows the update is successful, but nothing is actually added or deleted.
for example->your model associations are as follows: just follow the below steps to make use of magical _destroy attribute
####parent model
plan.rb
has_many :members,:dependent => :destroy
#this is important you want to destroy nested records
accepts_nested_attributes_for:members,:allow_destroy => true
attr_accessible :members_attributes
##child model
member.rb
belongs_to :plan
######controller
def edit
#plan.Plan.find(params[:id])
end
#####edit.html.erb
<%= form_for #plan do |f| %>
<%= f.fields_for :members do |member| %>
<div class="member">
<%= member.text_field :title%>
<%= image_tag 'delete.png',:class =>'remove_member',:id=>member.id %>
<!-- we need to set this hidden field value as 1 to mark it to be deleted during save/update of parent-->
<%= member.hidden_field :_destroy, :class => 'delete_member', :member_id => member.id %>
<!--similar to
<input id="plan_members_attributes_0__destroy" class="delete_member" type="hidden" value="false" name="plan[members_attributes][0][_destroy]" member_id="#{id of member}">
-->
</div>
<%end%>
<%end%>
##add js onclick of remove button/image
$('.delete_member').click(function(){
//remove div from screen
$(this).closest('.member').remove();
//get relevant id to remove/mark as delete
id =jQuery(this).attr('id');
//remove/mark the nested model/record as ready for deletion for rails by adding true/1 value
$("input[member_id="+id+"]").attr('value',1);
})

Nested form not appearing (simple_form)

I have a model named Page and one named Medium. Medium is for uploaded images. The two models are connected with a rich HABTM association.
I want to be able to upload images through my page edit form. However i got a strange result. I am only getting the file field when the page already have an image associated with it. What can cause this?
This is my page model:
class Page < ActiveRecord::Base
attr_accessible :content, :title, :url, :status, :excerpt, :media_attributes
belongs_to :users
has_many :page_medium
has_many :media, :through => :page_medium
accepts_nested_attributes_for :media
acts_as_url :title, :only_when_blank => true, :sync_url => true
acts_as_list
STATUS = %w[draft published]
def to_param
"#{url}" # or whatever you set :url_attribute to
end
validates :title, :status, :url, :presence => true
end
This is my form:
<%= simple_form_for #page, :html => { :class => 'page-form' } do |f| %>
<div class="span9">
<div class="row">
<%= f.input :title, input_html: { class: 'span6'}, wrapper_html: { class: 'span6'} %>
<%= f.input :url, input_html: { class: 'span3'}, wrapper_html: { class: 'span3'} %>
</div>
<div class="tabbable"> <!-- Only required for left/right tabs -->
<ul class="nav nav-tabs">
<li class="active">Content</li>
<li>Excerpt</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab1">
<%= f.input :content, label: false, input_html: { class: 'wysihtml5 span9' } %>
</div>
<div class="tab-pane" id="tab2">
<%= f.input :excerpt, label: false, input_html: { class: 'wysihtml5 span9 excerpt' }%>
</div>
</div>
</div>
</div>
<div class="span3">
<div class="well">
<fieldset>
<legend>Options</legend>
</fieldset>
<%= f.input :status , collection: Page::STATUS.map { |s| [s.humanize, s] }, prompt: '- Select page status -' %>
<div class="form-actions">
<%= f.button :submit, :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
pages_path, :class => 'btn' %>
</div>
</div>
</div>
<div class="span9">
<%= f.simple_fields_for :media do |m| %>
<%= m.input :image, as: :file %>
<% end %>
</div>
<% end %>
<%= render :partial => 'display_images' %>
Please comment if you need any more information or code. All tips, answers or constructive comments are appreciated. :)
this code:
<%= f.simple_fields_for :media do |m| %>
<%= m.input :image, as: :file %>
<% end %>
will only allow you to update the images you already have.
To add more images I suggest you follow this episode of railscast: http://railscasts.com/episodes/196-nested-model-form-revised

Resources