I'm trying to achieve the following scenario. Basically I have 2 models
Theme
Option
In my form I'm trying to let the user populate the Options table with set options, for example my html.erb would possibly look like this:
<%= form_for [:admin, @options],:class => 'form' do |f| %>
<div class="form-group">
<%= f.label :option[:color] %>
<%= f.text_field :option[:color], :class => 'form-control' %>
</div>
<div class="form-group">
<%= f.label :option[:header_title] %>
<%= f.text_field :option[:header_title], :class => 'form-control' %>
</div>
<div class="form-group">
<%= f.label :option[:footer_copy] %>
<%= f.text_field :option[:footer_copy], :class => 'form-control' %>
</div>
<div class="form-group">
<%= f.submit :value => 'Update', :class => 'btn btn-primary' %>
</div>
<% end %>
The above scenario will add 3 rows to the options table, each with the same theme_id.
But I'm unsure of the syntax to use for the controller (get and post actions), model and view
Any help will be appreciated! Thanks
EDIT When the above form example is saved it will save as 3 seperate rows in the options table for eg:
theme_id | name | value
1 | "color" | "green"
1 | "header_title" | "Theme title goes here"
1 | "footer_copy" | "lorem ipsum bla bla"
You need to generate these fields dynamically on the client side
class Theme < ActiveRecord::Base
has_many :options
accepts_nested_attributes_for :options, allow_destroy: true
end
class Option < ActiveRecord::Base
belongs_to :theme
end
class ThemesController < ApplicationController
def new
@theme = Theme.new
3.times { @theme.options.build }
end
private
def theme_params
params.require(:theme).permit(:title, options_attributes: [:id, :name, :value, :_destroy])
end
end
in your views/themes/_form.html.erb add
<%= form_for(@theme) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<%= f.fields_for :options do |builder| %>
<%= render 'option_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add", f, :options %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
create a new partials views/themes/_option_fields.html.erb
<fieldset>
<%= f.select :name, [["Color", "color"], ["Header", "header_title"], ["Footer", "footer_copy"]] %>
<%= f.text_field :value, placeholder: "Value" %>
<%= f.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
</fieldset>
in your themes.coffee file add
$(document).on "ready page:load", ->
$('form').on 'click', '.remove_fields', (event) ->
$(this).prev('input[type=hidden]').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
$('form').on 'click', '.add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
in application_helper.rb add
module ApplicationHelper
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
result
When I built a project similar to this, Options
had it's own controller with CRUD functionality.
The Video model:
theme.rb
has_many :options
The Option Model
option.rb
belongs_to :video
Options Controller:
def create
@option = Option.new(option_params)
@video.theme_id = @theme.id
if @option.save
...
end
Hope this is helpful.
You can use fields_for with a themes resource, controller and view, as follows:
# routes.rb
resources :themes
# app/controllers/themes_controller.rb
def new
@theme = Theme.new
end
def create
@theme = Theme.new theme_params
@theme.save!
[:color, :header_title, :footer_copy].each do |option_name|
theme.options.create! name: option_name, value: params[:theme][option_name]
end
end
private
def theme_params
params.require(:theme).permit(:title)
end
#app/views/themes/new.html.erb
<%= form_for @theme do |f| %>
<div>
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<% [:color, :header_title, :footer_copy].each do |option_name|
<%= f.fields_for option_name do |option_form| %>
<div>
<%= option_form.label option_name %>
<%= option_form.text_field option_name %>
</div>
<% end %>
<% end %>
<% end %>
You may add error checking, styling, etc. to the above, as needed.
Use rails built in associations and accepts_nested_attributes_for
:
class Theme < ActiveRecord::Base
has_many :options
accepts_nested_attributes_for :options, reject_if: :all_blank, allow_destroy: true
end
class Option < ActiveRecord::Base
belongs_to :theme
end
Use the cocoon gem to avoid writing all the JS code, examples in HAML, but you should get the point:
application.js
//= require cocoon
Your form:
= form_for @theme do |f|
.field
= f.label :title
%br
= f.text_field :title
%h3 Options
#option
= f.fields_for :options do |option|
= render 'options_fields', f: option
.links
= link_to_add_association 'add option', f, :options
= f.submit
_options_fields:
.nested-fields
.field
= f.label :name
%br
= f.text_field :name
.field
= f.label :value
%br
= f.text_field :value
= link_to_remove_association "remove option", f
Question
Do you want n fields (options) or simply these 3? cocoon is better suited for adding n options.
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