Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 4 saving fields in a similar manner to wordpress options table

I'm trying to achieve the following scenario. Basically I have 2 models

Theme

  • title

Option

  • theme_id
  • name
  • value

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"

like image 518
uguMark Avatar asked May 03 '16 16:05

uguMark


4 Answers

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

enter image description here

like image 172
PGill Avatar answered Oct 31 '22 18:10

PGill


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.

like image 21
Drew Reynolds Avatar answered Oct 31 '22 16:10

Drew Reynolds


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.

like image 27
Anand Avatar answered Oct 31 '22 17:10

Anand


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.

like image 43
Leonel Galán Avatar answered Oct 31 '22 18:10

Leonel Galán