In my Rails app I Pages, Blocks, BlockContents, Fields, and FieldContents.
The associations are:
class Page < ActiveRecord::Base
has_many :blocks
has_many :block_contents, :through => :blocks
end
class Block < ActiveRecord::Base
belongs_to :page
has_many :block_contents
end
class BlockContent < ActiveRecord::Base
belongs_to :block
has_many :field_contents
end
class Field < ActiveRecord::Base
has_many :field_contents
end
class FieldContent < ActiveRecord::Base
belongs_to :block_content
belongs_to :field
validates :content, presence: true
end
Using this I am able to have dynamic fields for Page.
However writing to the DB is currently done in the controller like so:
def update
@page = Page.find(params[:id])
if params[:field_content].present?
params[:field_content].each do |block_content|
block_content.last.each do |field|
field_content_row = FieldContent.where(block_content_id: block_content.first, field_id: field.first).first
if field_content_row.present?
field_content_row.update(content: field.last)
else
field_content = FieldContent.new
field_content.block_content_id = block_content.first
field_content.field_id = field.first
field_content.content = field.last
field_content.save
end
end
end
end
if @page.update_attributes(page_params)
redirect_to pages_path
else
render 'edit'
end
end
While this does in work, it's quite dirty and it doesn't show my validations for my FieldContent in the view (the validations do work as they don't save the FieldContent if they are invalid, but it should prevent the whole page from saving and show it in the view).
How can I make it so that I can show the validations for FieldContent?
For those that want to see the view:
<% @page.blocks.each do |block| %>
<% block.block_contents.each do |block_content| %>
<% field_group.fields.each do |field| %>
<input name="field_content[<%= block_content.id %>][<%= field.id %>]" id="field_content_<%= block_content.id %>_<%= field.id %>" type="text" value="<%= get_field_content(block_content, field.name) %>">
<% end %>
<% end %>
<% end %>
and the helper I use to get the content:
def get_field_content(block_content, field)
content = ''
block_content.field_contents.each do |field_content|
if field_content.field.name == field && field_content.block_content_id == block_content.id
content = field_content.content
end
end
content
end
Firstly what you need to do firstly in order to submit a form containing multiple models is to accept nested attributes.
In your page.rb file, put:
class Page < ActiveRecord::Base
has_many :blocks
has_many :block_contents, :through => :blocks
accepts_nested_attributes_for :field_contents
Secondly in the view - You must specify which fields are for which model using 'fields_for' So inside the form_for @page, you should put:
<%= f.fields_for :field_contents do |b| %>
Since :field_contents is the only object that will be updated along with @page. So that the form element in it's entirety looks like this:
<% @page.blocks.each do |block| %>
<% block.block_contents.each do |block_content| %>
<% field_group.fields.each do |field| %>
<%= form_for @page do |f| %>
<%= f.input :any attribute %>
<%= f.fields_for :field_contents do |b| %>
<%= b.text_field :content %>
<%= f.fields_for :block_contents do |b| %>
<%= b.hidden_field :block_content, value: "#{@block_content.id}"%>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
Since I've learnt you can already access @page.field_content, you don't have to touch anything in the controller as it updates the record automatically based on the existing relation.
So now that we have all models being submitted in a single request, you specified that you want error messages to show inside the view after validation has taken place. And this is the easy part.
From what I can see, you're currently validating the presence of :content
.
In your view: simply put a HTML tag or a rails content tag
<p class= "error"><%= "Your error message"if @field_content.errors[:content].present? %></p>
Let me know if you have any further questions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With