0

I'm working on a Rails app where user can "design" forms. See the image below:

enter image description here

For this I have the following models:

class ProgramPart < ApplicationRecord
  has_many :block_elements, dependent: :destroy

class BlockElement < ApplicationRecord
  belongs_to :program_part

  enum element_types: {
    rich_content: 0,
    textfield: 1,
    textarea: 2
  }

  has_rich_text :content

I'm not quite sure how to present the dynamic form to the user and what approach to use.

Currently having:

<%= form_with [how should my form look when inputs are dynamic?] %>
  <% @program_part.block_elements.each do |block| %>
      
    <% if block.element_type == BlockElement.element_types[:rich_content] %>
      <%= block.content %>
    <% elsif block.element_type == BlockElement.element_types[:textfield]  %>

      <%= block.question %>
      <!-- not sure how this should look -->
      <%= @form_builder.text_field(:answer_value)%>
        
    <% elsif block.element_type == BlockElement.element_types[:textarea]  %>
      <!-- textarea -->
    <% end %>

  <% end %>

<% end %>

Have anyone implemented something similar or knows any good resource for it?

1
  • Instead of block.element_type == BlockElement.element_types[:rich_content] which is very long and ugly just use the inquisition methods provided by the enum macro. if block.rich_content?. Commented Sep 7, 2023 at 22:30

1 Answer 1

2

I have implemented similar thing with forms configurable by admin and then used by client. Here are the basics:

  1. configure templates for every possible DOM input
  2. make a page to create a PageTemplate - PageTemplate is the abstract object that will contain inputs configured in step 1.
  3. make a form that uses a JS to populate PageTemplates - for example there is a button "Add text field" and by pressing it you are adding an imput to the form with JS.
  4. after submitting populated PageTemplate you can render it for the user.

You need an abstract Page object, abstract DOM object and then an object for each DOM element like this:

class PageTemplate
  has_many :dom_objects
end

class DomObject
  self.table_name = 'dom_objects'
  belongs_to :page_template
  
  def dom_type
    nil
  end
end

class TextField < DomObject
  self.table_name = 'dom_objects'

  def dom_type
    'text_field'
  end
end
class TextArea < DomObject
  self.table_name = 'dom_objects'

  def dom_type
    'text_area'
  end
end

The dom_type will be the thing that will tell your system what type of object it needs to render. Of cause it also should be stored in DB but it helps you to automatically fill the database field with the type if you are adding a text_field object instead of something abstract. As you see every static dom object nestes from abstract DomObject - which is basically the table of all dom objects created. you can even add such info as width, height or additional css classes as parameters to the dom object table, making every created dom input a uniq and higly customizable entity.

Wrapup

This approach is Object oriented and Composite by design. All you need to do is set up each 'brick' and then assemble the 'wall' dynamically with JS and it will be stored in DB neatly. then just get the page object from db and render it wherever you need it.

Hope it helps! :)

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.