简体   繁体   中英

Change form multiple partials

I'm following this railscast on nested forms to create a similar application.

In Ryan's application he has a structure like:

  1. Surveys ---> 2. Questions ---> 3. Answers

So a user can create a survey and add/delete questions, with corresponding answers that they can add/delete.

I'm trying to do it a little differently. I have:

  1. Surveys ---> 2. Questions ---> 3. Answers Or Comment

For my application, after a user has created a question, the user can create an answer (just like Ryan's app). But there is a select box next to each answer, with the default being "Answer" and the other being "Comment". I'm trying to get basically the same functionality of Ryan's app, but with two tables: comments table and answers table.

My code mimics the railscast, except for my answer_fields partial looks like:

<fieldset>
   <%= f.label :content, "Answer" %>
   <%= f.select :switch_answer_table options_for_select(["Answer", "Comment"]), class: "change-type" %>
   <%= f.text_field :content %>
   <%= f.check_box :_destroy %>
   <%= f.label :_destroy, "remove" %>
</fieldset>

and I have a comment_fields partial that looks like:

<fieldset>
   <%= f.label :content, "Comment" %>
   <%= f.select :switch_answer_table options_for_select(["Comment", "Answer"]), class: "change-type" %>
   <%= f.text_field :comment_content %>
   ... (leaving out some stuff) ...
   <%= f.check_box :_destroy %>
   <%= f.label :_destroy, "remove" %>
</fieldset>

My question.rb model looks like:

class Question < ActiveRecord::Base
  belongs_to :survey
  has_many :answers
  has_many :comments
  accepts_nested_attributes_for :answers, allow_destroy: true
  accepts_nested_attributes_for :comments, allow_destroy: true
end

I've been working on this, and I'm stuck. My jQuery only looks like this so far:

jQuery ->
   $('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()

   $('form').on 'change', '.change-type', (event) ->
     # Confused on how to change partials

Basically, I need a way to onChange, the partial for the selected answer/comment.

alejandro babio is correct. render both partials. give one of them a style of display:none and then toggle the class in your coffescipt onchange handler. In the case that a user starts to type an answer and then switches to a comment you'll also want to clear the fields associated with the newly hidden fields otherwise on submit you could have both comment and answer submitted.

I haven't tested this, but perhaps you want to use dependent-fields-rails . Depending on the state of the select box you can then show either the comment box or answer box.

The example given on the github page (uses simple form, but you can apply the same to your form_for):

= simple_form_for(@filing) do
    = f.input :registration_office, collection: ['habm', 'dpma']

    .js-dependent-fields[data-select-id='filing_registration_office' data-option-value='habm']
        = f.input :language, collection: ['english', 'german']

Then I guess you use fields_for to satisfy your has_many relationship for example:

f.select :switch_answer_table, collection: ['answer', 'comment']
   .js-dependent-fields[data-select-id='question_switch_answer_table' data-option-value='answer']
      f.fields_for :answers do |abuilder|
      ...
   .js-dependent-fields[data-select-id='question_switch_answer_table' data-option-value='comment']
      f.fields_for :comments do |cbuilder|
      ...

Again, not tested and a best guess but it hopefully nudges you into the right direction.

First at all, you must render both partials (answer_fields and comments_fields) with the form.

At the comments_partial replace <fieldset> with <fieldset style="display:none">

Change visibility of partials with coffeescript:

$('form').on 'change', '.change-type', (event) ->
  $('.change-type').each ->
    $(@).closest('fieldset').each ->
      if $(@).is(':visible')
        $(@).find('input').val('')           # reset all filels to empty values
      $(@).toggle()

I thing that do the trick.

I clear only input fields, but there must be all of them to prevent save de hidden element (like says hraynaud)

The partial fields are generated as a data attribute by the link_to_add_fields helper method. The js then gets the data object and renders it. Consider the following (this is untested but should be a good guide):

  1. The easiest way would be to have seperate 'Add Answer' and 'Add Comment' links with seperate helper methods, or
  2. Allow the user to select Answer or Comment before clicking the link to add and have each different partial as a different data attribute. eg

_form.html.erb

<select id="response_type_select_field">
  <option value="comment">Comment</option>
  <option value="answer">Answer</option>
</select>

<%= link_to_add_reply_fields "Add Reply", f, :answers, :comments %>

surveys_helper.rb

module SurveysHelper
  def link_to_add_reply_fields(name, f, association_1, association_2)
    object_1 = f.object.send(association_1).klass.new
    object_1_id = object_1.object_id
    object_1_fields = f.fields_for(association_1, object_1, child_index: object_1_id) do |builder|
      render(association_1.to_s.singularize + "_fields", f: builder)
    end

    object_2 = f.object.send(association_2).klass.new
    object_2_id = object_2.object_id
    object_2_fields = f.fields_for(association_2, object_2, child_index: object_2_id) do |builder|
      render(association_2.to_s.singularize + "_fields", f: builder)
    end

    link_to(name, '#', class: "add_reply_fields", data: {id_1: object_1_id, id_2: object_2_id, object_1_fields: object_1_fields.gsub("\n", ""), object_2_fields: object_2_fields.gsub("\n", "")})
  end
end

Coffee script:

jQuery ->
  $('form').on 'click', '.add_reply_fields', (event) ->
    time = new Date().getTime()
    reply_type = $("#response_type_select_field option:selected").val()

    if reply_type is 'answer'
      data_attribute = 'object_1_fields'
      regexp = new RegExp($(this).data('id_1'), 'g')
    else if reply_type is 'comment'
      data_attribute = 'object_2_fields'
      regexp = new RegExp($(this).data('id_2'), 'g')

    $(this).before($(this).data(data_attribute).replace(regexp, time))
    event.preventDefault()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM