Как создать неограниченное количество объектов из одного события отправки в ruby on rails

#ruby-on-rails

#ruby-on-rails

Вопрос:

Я новичок в ruby, мне нужно создать форму для регистрации пользователя, и пользователь должен иметь возможность регистрировать неограниченное количество друзей. Хочу сделать это вот так: начните с одной формы и add кнопки. Если пользователь нажимает на add кнопку, первая форма копируется и так далее. В итоге остается только одна кнопка отправки, которая отправляет все формы. Пытаюсь это сделать, но не могу понять, как передать неограниченное количество объектов в форму. Теперь просто передаем один объект:

participation_forms_controller.rb :

 class ParticipationFormsController < ApplicationController
  def new
    @participation_form = ParticipationForm.new
  end
  ...
  

Проблема в том, что мне нужно создать некоторое количество ParticipationForm объектов, но фактическое количество этих объектов определяет только пользователь. И, как я понимаю, мне нужно передать такое же количество объектов из ruby controller в мой html. Но я не знаю, сколько, поэтому я не могу выполнить какой-то цикл в контроллере подобным образом:

 class ParticipationFormsController < ApplicationController
  def new
    @participation_forms = []
    <dont_know_how_many>.times do
    @participation_forms << ParticipationForm.new
    end
  end
  

Я думал о каком-то грязном взломе: скопируйте все данные из других форм в первую, а затем отправьте только первую. Но я хочу сделать это хорошим способом, как это делают обычные программисты. Что я могу сделать, если бы я использовал python : просто создайте одну форму с именами полей, такими как username_1 , userdata_1 , username_2 , userdata_2 и так далее, затем просто проанализируйте их на серверной части. Но ruby несколько строг в отношении того, какие параметры я могу передавать контроллеру, я должен предопределять их, поэтому не могу выполнить этот трюк? Что такое мой create контроллер:

   def create
    @participation_form = ParticipationForm.new(article_params)

    @participation_form.save
    render 'new'
  end

  private
    def article_params
      params.require(:participation_form).permit(
        :name, :telephone, :email, :post, :image)
    end
  

Итак, как я могу передать туда что-то вроде name_999 ? Ни в коем случае, ruby является строгим.
Было бы здорово, если бы в ruby я мог использовать regexp для разрешения набора полей, просто мой фантастический код:

 def article_params

  params.require(:participation_form).permit(
    :re.match('^name_d*$'))
  end
  

Если это будет реально, я могу использовать одну форму для этой задачи.

фрагмент кода интерфейса «чего-я-хочу»

 /* Latest compiled and minified JavaScript included as External Resource */jQuery(document).ready(function() {

  var addFriendForm = function() {
    $('.button-group__add-friend-btn').click(function() {
      forms = $(".form-signin");
      var first = forms.first();
      var first_clone = first.clone();
      first_clone_children = first_clone.children();
      $(first_clone_children).each(function() {
        if ($(this).hasClass('form-signin__file-upload')) {
          file_upload_children = $(this).children();
          file_upload_children.each(function() {
            this.value = '';
          });
        }
        else {
          this.value = '';
        }
      });
      var last = forms.last();
      first_clone.insertAfter(last);
      $('<hr class="container__hr">').insertAfter(last);
      changeRegBtnText();
      addDelBtn();
    });
  };
  
  var changeRegBtnText = function(back_to_one=false) {
    var one = 'register'
    var plural = 'register with friends';
    var btn = $('.button-group__add-reg-btn');
    if (btn.text() == one) {
      btn.html(plural);
    }
    else if (back_to_one) {
      btn.html(one);
    }
  };
  
  var addDelBtn = function() {
    var del_btn = $("#friend_del_btn");
    if (del_btn.hasClass("button-group__del-friend-btn_disabled")) {
      del_btn.removeClass("button-group__del-friend-btn_disabled");
      del_btn.addClass("button-group__del-friend-btn_active");
    }
  };

  var deleteFriendForm = function() {
    $('#friend_del_btn').click(function() {
      forms = $(".form-signin");
      hrs = $('.container__hr');
      var last_fieldset = forms.last();
      last_fieldset.remove();
      var last_hr = hrs.last();
      last_hr.remove();
      if ($(".form-signin").length == 1) {
        changeRegBtnText(back_to_one=true);
        $("#friend_del_btn").addClass("button-group__del-friend-btn_disabled");
      }
    });
  };

  var submitForms = function() {
    $(".button-group__add-reg-btn").on("click", function(e) {
      $(".form-signin").each(function(){
        $(this).trigger("submit");
        console.log('submit');
      });
    });
  }

  addFriendForm();
  deleteFriendForm();
  submitForms();

});  
 .form-control {
  margin-top: 10px;
  margin-bottom: 10px;
  height: auto;
  padding: 10px;
  font-size: 16px;
}

.form-signin {
  max-width: 330px;
  padding: 15px;
  margin: 0 auto;
}

.container__hr {
  background-color: #A9A9A9;
  height: 1px;
  width: 300px;
}

.form-signin__file-upload {
  position: relative;
  overflow: hidden;
  margin-bottom: 10px;
  height: 35px;
  width: 100%;
}

.form-signin__file-upload-filename {
  float: left;
  color: #222;
  margin-top: -3px;
}

.form-signin__file-upload-input {
  position: absolute;
  top: 0;
  right: 0;
  margin: 0;
  padding: 0;
  cursor: pointer;
  opacity: 0;
  filter: alpha(opacity=0);
}

.form-signin__file-upload-span {
  line-height: 1.42;
  color: black;
}

.button-group {
  margin-left: auto;
  margin-right: auto;
  max-width: 330px;
  padding-left: 15px;
  padding-right: 15px;
}

.button-group__add-friend-btn,
.button-group__add-reg-btn {
  margin-top: 10px;
  margin-bottom: 10px !important;
  width: 100%;
}

.button-group__del-friend-btn_disabled {
  display: none !important;
}

.button-group__del-friend-btn_active {
  margin-top: 10px;
  margin-bottom: 10px !important;
  width: 100%;
}  
 <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
  <h3 class="text-center">Register to conference</h3>
  <form class="form-signin">
    <input type="text" name="[participation_form]name" class="form-control" placeholder="Имя" required autofocus>
    <input type="tel" name="[participation_form]telephone" class="form-control" placeholder="Телефон" required>
    <input type="email" name="[participation_form]email" class="form-control" placeholder="Електропочта" required>
    <input type="text" name="[participation_form]post" class="form-control" placeholder="Должность" required>
    <div class="form-signin__file-upload btn btn-info" disabled="disabled">
      <input class="form-signin__file-upload-filename">
      <input type="file" name="[participation_form]image" class="form-control form-signin__file-upload-input" required>
      <span class="form-signin__file-upload-span">аватар</span>
    </div>
  </form>
  <div class="text-center button-group">
    <button type="button" class="btn btn-default button-group__add-friend-btn">
      <span class="glyphicon glyphicon-plus"></span> add friend
    </button>
    <button type="button" id="friend_del_btn" class="btn btn-default button-group__del-friend-btn_disabled">
      <span class="glyphicon glyphicon-remove"></span> del friend
    </button>
    <button class="btn btn-lg btn-primary btn-block button-group__add-reg-btn" type="submit">register</button>
  </div>  

Комментарии:

1. Круто. итак … не могли бы вы, пожалуйста, включить свой код в свой вопрос? jsfiddle — это потрясающе, и здорово, что вы его предоставили, но мы также хотим, чтобы наши вопросы Stack Overflow выдержали испытание временем, а ссылки на jsfiddle довольно быстро устаревают. Во-вторых: с какой проблемой / error / problem вы столкнулись? т. е. что вы наблюдаете, и что вы ожидаете наблюдать вместо этого? Как вы думаете, почему вам нужно передавать неограниченное количество объектов в форму? разве в форме нет просто полей, а затем она передает эти поля вашему контроллеру, который выполняет фактическое создание объекта?

2. по сути, это почти весь код ruby, который у меня есть.

3. Код, который находится в вашей скрипке, тоже является кодом 🙂 Также — есть ли у вас частичные шаблоны в html или html.erb ? включите их тоже — это часть того, что мы сделаем, чтобы помочь вам 🙂 Включите в свой Question список все, что имеет отношение к вопросу, чтобы мы могли посмотреть и выяснить, что нужно изменить… (мы также поиграем с кодом в fiddle, чтобы разобраться в этом).

4. У меня есть один базовый application.html.erb макет и другой html для формы — вы можете увидеть его в jsfiddle. Приложение состоит из одной страницы (регистрационной формы).

5. Хорошо, я сделаю, скопируйте это

Ответ №1:

Итак, это, вероятно, не полное решение, но мое первое, на что следует обратить внимание, это… вам должен понадобиться всего один ParticipationForm — вам не нужно добавлять несколько форм, просто несколько наборов полей сведений о пользователе.

Каждый набор пользовательских полей необходимо будет уникально сгруппировать и идентифицировать, например, конечный результат (после того, как javascript каждый раз добавит новые поля) будет примерно таким, как показано ниже (примечание: я только что добавил очень простую версию полей, а не ту, которую вы в конечном итоге будете использовать со всеми подробностями):

 <form class="form-signin">
  <div class="user-field-set">  
   <input type="text" name="[participation_form][1]name" class="form-control" placeholder="Имя" required autofocus>
   <input type="tel" name="[participation_form][1]telephone" class="form-control" placeholder="Телефон" required>
   <input type="email" name="[participation_form][1]email" class="form-control" placeholder="Електропочта" required>
  </div>
  <div class="user-field-set">  
   <input type="text" name="[participation_form][2]name" class="form-control" placeholder="Имя" required autofocus>
   <input type="tel" name="[participation_form][2]telephone" class="form-control" placeholder="Телефон" required>
   <input type="email" name="[participation_form][2]email" class="form-control" placeholder="Електропочта" required>
  </div>
*** submit button for all sets of user details goes here ***
</form>
  

Комментарии:

1. Да, это один из способов сделать это, цените это. Но не могли бы вы, пожалуйста, также предоставить некоторый код в create контроллере для такого типа компоновки данных?

2. Шаг 1) обновите свой javascript, чтобы вставить наборы полей с уникальными идентификаторами, подобными этому. Шаг 2) отправьте форму и посмотрите, какие параметры передаются вам в вашем контроллере, затем шаг 3) мы посмотрим, какой код нам нужен для анализа этого набора параметров 🙂

3. Обратите внимание, что стандартная практика Rails заключается в использовании form_for с несколькими fields_for , а затем использовании accepts_nested_parameters_for :user (или эквивалентной модели, добавляемой здесь) для приема нескольких пользовательских данных в контроллере… styart с фиксированным количеством пользователей (вместо их динамического добавления). Затем адаптируйте html, сгенерированный fields_for , для создания вашего javascript dynamic-field-adder… по крайней мере, так я делал это в прошлом

4. О, name="[participation_form][1]name" это какой-то массив, заполняемый в html? Это действительно работает) Большое спасибо

5. Да 🙂 это то, что rails часто делает с этими наборами полей. Затем вы можете просто выполнить итерацию по массиву в вашем контроллере (хотя часто бывает полезно увидеть точную форму параметров, которые вы получаете, прежде чем пытаться угадать, как это произойдет — избавляет от головной боли) 🙂