2017-01-05 5 views
6

В моем приложении Rails I Pages, Blocks, BlockContents, Fields и FieldContents.Сохранение динамических полей в Rails

Ассоциации являются:

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 

Используя это, я могу иметь динамические поля для страницы.

Однако запись в БД в настоящее время осуществляется в контроллере так:

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 

В то время как это делает в работе, это довольно грязная и не показывать мои валидаций для моего FieldContent в представлении (проверки выполняются, поскольку они не сохраняют полевой элемент, если они недействительны, но это должно препятствовать сохранению всей страницы и отображать ее в представлении).

Как я могу сделать это так, чтобы показывать подтверждение для FieldContent?


Для тех, кто хочет увидеть вид:

<% @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 %> 

и помощника я использую, чтобы получить содержание:

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 
+0

Я сделал что-то подобное в приложении 5 лет назад, которое все еще может быть полезно. Не стесняйтесь проверять другие части, но [соответствующий лакомый кусочек здесь] (https://github.com/coreyward/talent-releases/blob/master/app/concerns/response_form.rb) с контроллером, интегрирующим его [здесь ] (https: // GitHub.ком/coreyward/талант-релизы/BLOB/Master/приложение/контроллеры/releases_controller.rb # L149-L153). – coreyward

+0

@coreyward Это выглядит интересно, но я довольно новичок в Ruby и Rails, вы могли бы дать ответ с более подробным объяснением того, что делает ваш код. Я предполагаю, что ваша проблема связана с вашими моделями, так что сохранение происходит одновременно на обеих моделях? – Cameron

+0

@coreyward Я обновил свой пост, чтобы показать, как выглядит этот вид. – Cameron

ответ

2

Во-первых, то, что вам нужно сделать, во-первых, для того, чтобы отправить форму, содержащую несколько моделей, - это принять вложенные атрибуты.

В файле page.rb, говоря:

class Page < ActiveRecord::Base 
has_many :blocks 
has_many :block_contents, :through => :blocks 
accepts_nested_attributes_for :field_contents 

Во-вторых, в представлении - Вы должны указать, какие поля, для которых модель с использованием «fields_for» Так внутри form_for @Page, вы должны поставить:

<%= f.fields_for :field_contents do |b| %> 

С: field_contents - единственный объект, который будет обновляться вместе с @page. Так что элемент формы в полном комплекте выглядит следующим образом:

<% @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 %> 

Так как я узнал, вы уже можете получить доступ к @ page.field_content, вы не должны ничего трогать в контроллере, как он обновляет запись автоматически основанный на существующем отношении.

Итак, теперь, когда все модели представлены в одном запросе, вы указали, что сообщения об ошибках должны отображаться внутри представления после того, как была выполнена валидация. И это легкая часть.

Из того, что я вижу, вы в настоящее время проверяете наличие :content.

На вашем взгляде: просто поставить HTML-тег или рейки содержания тега

<p class= "error"><%= "Your error message"if @field_content.errors[:content].present? %></p> 

Позвольте мне знать, если у вас есть какие-либо дополнительные вопросы.

+0

Я не буду следовать за вами с частью обзора. Не могли бы вы расширить свой пример, чтобы показать, как выписываются поля? Благодарю. – Cameron

+0

обновил мой ответ – Crashtor

+0

Где находится '@ blocks'? Единственной переменной, которую я имею в контроллере, является '@ page'. Создает '@ page.blocks.build'' @ blocks'? и мне также нужно сделать это для FieldContents и BlockContents в контроллере? – Cameron

Смежные вопросы