简体   繁体   中英

How to pre-populate dynamically added form inputs in Rails

I have an edit.html.erb form for @profile instances. Normally when I load the form, the inputs inside the form are pre-populated with the corresponding attribute of the instance.

eg <%= f.text_field :name %> would pre-populate its value from @profile.name .

I would like to add additional fields on runtime using jQuery that allows users to input additional data with additional dynamically added inputs.

ie @profile.subjects is a hash containing the subjects and their descriptions (for example it could be {"math" => "I teach calculus", "science" => "I have a physics degree"} )

I retrieve a list of subjects from @profile.subjects , and insert textarea elements into the form for each subject using append() in jQuery.

So if @profile.subjects contains "math" and "science", I would do the following in jQuery:

var subject = *string containing subject*;
parent.append("<textarea id='profile_" + subject + "_desc' name='profile[" + subject + "_desc]'></textarea");

This would imitate creating the fields <%= f.text_area :math_desc %> and <%= f.text_area :science_desc %> to my knowledge.

However, when I pass in the attributes math_desc and science_desc to the instance variable @profile in my controller, the inputs do not pre-populate with their values unlike the static form inputs.

I can access @profile.math_desc and @profile.science_desc in the view, but I would like the inputs to have these values upon the view loading.

I do not know how I would add ruby variables to the append() argument with a variable as the attribute name. eg append("<textarea><%= @profile.send('math_desc') %></textarea>") works, but append("<textarea><%= @profile.send('" + subject + "_desc') %></textarea>") does not.

EDIT 1:

Here is how I assigned additional attributes to my instance:

# NOTE: @profile.subjects contains a string representation of {"Math" => "I teach calculus", "Science" => "I have a physics degree", ...}

  def edit
     @tutor = current_tutor
     @profile = @tutor.profile
     # Call method to parse subjects JSON
     subject_parsing(@profile)
  end 

  private 
  # Decode JSON subject field into a hash
  def subject_parsing(profile)
    unless profile.subjects.blank?
      # Decode subjects into a hash
      parsed_subjects = ActiveSupport::JSON.decode(profile.subjects)
      # Extract subject names from hash into a string separated by commas
      profile.subject_names = parsed_subjects.keys.join(', ')

      parsed_subjects.each do |subj, desc| 
        subj = subj.downcase
        profile.class_eval do
          attr_accessor "#{subj}_desc"
        end
        profile.send("#{subj}_desc=", desc)
      end
    end
  end

So my workaround was to use the gon gem to send a Rails hash to Javascript then calling the subject descriptions according to the subject_names array.

Added this to the controller (see EDIT 1 code)

  # Decode JSON subject field into a hash
  def subject_parsing(tutor_profile)
    unless tutor_profile.subjects.blank?
      # Decode subjects into a hash TODO: change this because it is very slow
      gon.subjects = ActiveSupport::JSON.decode(tutor_profile.subjects)
      # Extract subject names from hash into a string separated by commas
      tutor_profile.subject_names = gon.subjects.keys.join(', ')
    end
  end

Then ended up with this in the Javascript:

var subjectNamesInput = $('#tutor_profile_subject_names');

// Initialize subject description input boxes
$.each(subjectNamesInput.tagsinput('items'), function(i, subject){
    updateSubjectInputs(subject, 'add');
});

function updateSubjectInputs(subject, action){
    var parent = $('#subject-description-inputs');

    var subject = escape(subject);

    var text = "";

    if (gon.subjects[subject] != undefined)
        text = gon.subjects[subject];

    if (action == 'add'){
        parent.append("<textarea id='tutor_profile_" + subject.toLowerCase() + "_description' name='tutor_profile[" + 
            subject.toLowerCase() + "_description]' class='form-control form-text-area' rows='5' placeholder='Add some detail about your experience with " + subject + 
            " (optional)'>" + text + "</textarea>");
    }
    else if (action == 'remove'){
        $('#tutor_profile_' + subject.toLowerCase() + '_description').remove();
    }
}

This was in my view:

<%= f.text_field :subject_names, class: 'form-control tagsinput typeahead', placeholder: 'Subjects you teach' %>

<div id="subject-description-inputs">
</div>

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