#ruby #stimulusjs
#ruby #stimulusjs
Вопрос:
У меня есть регистрационная форма, которая позволяет директорам турниров выбирать, какие поля они будут запрашивать при регистрации участников на турнир. Я следил за видео на Go Rails, чтобы добавить динамические вложенные поля формы в форму с помощью stimulum JS, и это отлично работает.
Я пытаюсь добавить вторую группу полей в другой раздел формы. форма разбита на разделы, основную информацию, контактную информацию, информацию о соревнованиях и т. Д., И я хотел бы иметь возможность добавлять пользовательские поля в каждый раздел.
В базовом разделе это отлично работает. Я могу добавлять и удалять пользовательские поля, но когда я добавил его в раздел контактов, даже с изменением вложенной формы на вложенные контакты, он просто пульсирует в полях, которые я создаю в базовом разделе. Я могу добавлять и удалять из любого раздела, но он просто показывает их все в обоих разделах.
Вот моя форма, как она есть сейчас. Мне все еще нужно добавить другие разделы, но я пытался заставить работать базовые и контактные пользовательские поля, прежде чем добавлять остальную часть формы:
<h4>Athlete Information</h4>
<%= form_with(model: @event, class: "shadow p-3 mb-3 rounded text-dark", local: true) do |f| %>
<%= f.hidden_field :athlete_info_complete, value: 1 %>
<%= f.hidden_field :next, value: 6 %>
<div class="row club_container">
<div class="col-12">
<legend>
Check all the boxes next to the information you would like to
request from your tournament participants. Each area will have an option
to add custom fields.
</legend>
<br><br>
<h4>Basic Information</h4>
<div class="form-group row">
<div class="col-md-9 col-sm-12">
<label class="main">First Name
<%= f.check_box :first_name %>
<span class="w3docs"></span>
</label>
<label class="main">Last Name
<%= f.check_box :last_name %>
<span class="w3docs"></span>
</label>
<label class="main">Date of birth
<%= f.check_box :dob %>
<span class="w3docs"></span>
</label>
</div>
</div>
<h4>Custom Fields</h4>
<small>
Enter the name of your custom field you plan to offer.
</small>
<br><br>
<div data-controller="nested-form">
<template data-target="nested-form.template">
<%= f.fields_for :fields, Field.new, child_index: 'NEW_RECORD' do |field| %>
<%= render 'events/forms/custom_fields_basic', f: field %>
<% end %>
</template>
<%= f.fields_for :fields do |field| %>
<%= render 'events/forms/custom_fields_basic', f: field %>
<% end %>
<div class="mb-3" data-target="nested-form.links">
<%= link_to 'Add Custom Field', "#", class: "btn btn-outline-dark", data: { action: "click->nested-form#add_association" } %>
</div>
</div>
<h4>Contact Information</h4>
<div class="form-group row">
<div class="col-md-9 col-sm-12">
<label class="main">Address 1
<%= f.check_box :address1 %>
<span class="w3docs"></span>
</label>
<label class="main">Address 2
<%= f.check_box :address2 %>
<span class="w3docs"></span>
</label>
<label class="main">City
<%= f.check_box :city %>
<span class="w3docs"></span>
</label>
</div>
</div>
<div data-controller="nested-contacts">
<template data-target="nested-contacts.template">
<%= f.fields_for :fields, Field.new, child_index: 'NEW_RECORD' do |field| %>
<%= render 'events/forms/custom_fields_contact', f: field %>
<% end %>
</template>
<%= f.fields_for :fields do |field| %>
<%= render 'events/forms/custom_fields_contact', f: field %>
<% end %>
<div class="mb-3" data-target="nested-contacts.links">
<%= link_to 'Add Custom Field', "#", class: "btn btn-outline-dark", data: { action: "click->nested-contacts#add_association" } %>
</div>
</div>
<div class="form-group row">
<div class="col-md-9 col-sm-12"></div>
<div class="col-md-3 col-sm-12">
<button id="button_next" class="profile_btn align-middle" style="width:180px;">Finalize <span style="margin-top: 5px; font-size: 18px;"><i class="fas fa-long-arrow-alt-right"></i></span></button>
</div>
</div>
</div>
</div>
<% end %>
Вот _custom_fields_basic.html.erb
<%= content_tag :div, class: 'nested-fields', data: { new_record: f.object.new_record? } do %>
<div class="form-group row">
<div class="col-md-3 col-sm-12 col-form-label">
<%= f.label :field_name %>
</div>
<div class="col-md-9 col-sm-12">
<%= f.text_field :name, class: 'form-control' %>
<small><%= link_to 'Remove', '#', data: { action: 'click->nested-form#remove_association' } %></small>
</div>
</div>
<%= f.hidden_field :field_type, value: 'basic' %>
<%= f.hidden_field :_destroy %>
<% end %>
Это файл custom_fields_contact.html.erb:
<%= content_tag :div, class: 'nested-contacts', data: { new_record: f.object.new_record? } do %>
<div class="form-group row">
<div class="col-md-3 col-sm-12 col-form-label">
<%= f.label :field_name %>
</div>
<div class="col-md-9 col-sm-12">
<%= f.text_field :name, class: 'form-control' %>
<small><%= link_to 'Remove', '#', data: { action: 'click->nested-contacts#remove_association' } %></small>
</div>
</div>
<%= f.hidden_field :field_type, value: 'contact' %>
<%= f.hidden_field :_destroy %>
<% end %>
Вот nexted_form_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "links", "template" ]
connect() {
}
add_association(event) {
event.preventDefault()
var content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime())
this.linksTarget.insertAdjacentHTML('beforebegin', content)
}
remove_association(event) {
event.preventDefault()
let wrapper = event.target.closest(".nested-fields")
if (wrapper.dataset.newRecord == "true") {
wrapper.remove()
} else {
wrapper.querySelector("input[name*='_destroy']").value = 1
wrapper.style.display = 'none'
}
}
}
И nested_contacts_controller.js:
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "links", "template" ]
connect() {
}
add_association(event) {
event.preventDefault()
var content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime())
this.linksTarget.insertAdjacentHTML('beforebegin', content)
}
remove_association(event) {
event.preventDefault()
let wrapper = event.target.closest(".nested-contacts")
if (wrapper.dataset.newRecord == "true") {
wrapper.remove()
} else {
wrapper.querySelector("input[name*='_destroy']").value = 1
wrapper.style.display = 'none'
}
}
}
Если есть способ сделать это с помощью одного контроллера, это было бы здорово, но я согласен с наличием отдельных контроллеров для каждого раздела, если это необходимо.
Спасибо
Ответ №1:
Ваша проблема не в контроллерах стимулов, а в том, как вы определяете эти поля. Однако следует отметить, что Stimulus должен быть модульным, поэтому вам нужен только один контроллер, и вы можете реализовать его столько раз, сколько захотите, на одной странице. Цель data-controller
состоит в том, чтобы определить, на какую часть DOM может влиять контроллер.
# form
<div data-controller="nested-form">
Affects only things in here
</div>
<div data-controller="nested-form">
Affects only things in here
</div>
Проблема в том, что вы используете одну и ту же связанную модель в двух местах. При отображении страницы редактирования форма будет отображать все поля в каждом разделе, потому что вы запрашиваете это с помощью этих строк
<%= f.fields_for :fields do |field| %> #specifically this one
<%= render 'events/forms/custom_fields_basic', f: field %>
<% end %>
Это вызывает все связанные модели, связанные с событием, и отображает поля для них.
У вас есть скрытое поле для field_type
, поэтому теперь вам просто нужно определить некоторые методы в модели событий.
# model
def basic_fields
self.fields.where('field_type = ?', 'basic')
end
Теперь вместо <%= f.fields_for :fields do |field| %>
этого вы делаете <%= f.fields_for :basic_fields do |field| %>
и так далее.