Skip to content

Instantly share code, notes, and snippets.

@monorkin
Created November 10, 2020 10:57
Show Gist options
  • Save monorkin/1ff71aaf886b386d68486b64fb984fcc to your computer and use it in GitHub Desktop.
Save monorkin/1ff71aaf886b386d68486b64fb984fcc to your computer and use it in GitHub Desktop.
Stimulus has_many fields_for controller
= f.simple_fields_for :item do |ff|
.row.mt-4
.col-12
label Categories
.preview_images data-controller="has-many-fields-for"
.preview_images__container data-target="has-many-fields-for.container"
= ff.simple_fields_for :taggings do |ff|
= render 'category_tagging_form', f: ff
script type="text/html" data-target="has-many-fields-for.template" data-index-placeholder="new_tagging"
= ff.simple_fields_for :taggings, ff.object.taggings.build, child_index: 'new_tagging' do |ff|
= render 'category_tagging_form', f: ff
.preview_images__actions
.btn.btn-block.btn-secondary data-action="click->has-many-fields-for#add"
i.fas.fa-plus
| Add new category
import { Controller } from "stimulus"
export default class extends Controller {
static targets = ["template", "container"]
connect() {
if (!this.hasContainerTarget) {
return
}
if (!this.hasTemplateTarget && !this.element.dataset.template) {
return
}
this.childElementClass =
`hmff-ele-${Math.random().toString(16).substring(7)}`
this.template = this.element.dataset.template ||
this.templateTarget.innerHTML
this.placeholder = this.element.dataset.indexPlaceholder ||
this.templateTarget.dataset.indexPlaceholder
if (this.hasTemplateTarget) {
this.templateTarget.remove()
}
this.normalizeExistingChildren()
this.reindexElements()
}
add(event) {
event.preventDefault()
if (!this.template) {
return
}
this.addElement()
}
remove(event) {
let wrapper = this.closestWrapper(event.target)
if (!wrapper) {
return
}
var destroyFlagInput = null
wrapper.querySelectorAll('input').forEach(function(element) {
if (destroyFlagInput) {
return
}
let name = element.getAttribute('name')
if (name && name.endsWith('[_destroy]')) {
destroyFlagInput = element
return
}
})
if (!destroyFlagInput) {
wrapper.remove()
return
}
destroyFlagInput.setAttribute('value', 'true')
wrapper.style.display = 'none'
}
moveUp(event) {
let wrapper = this.closestWrapper(event.target)
if (!wrapper) {
return
}
if(!wrapper.previousElementSibling) {
return
}
wrapper.parentNode.insertBefore(wrapper, wrapper.previousElementSibling)
this.reindexElements()
}
moveDown(event) {
let wrapper = this.closestWrapper(event.target)
if (!wrapper) {
return
}
if(!wrapper.nextElementSibling) {
return
}
wrapper.parentNode.insertBefore(wrapper.nextElementSibling, wrapper)
this.reindexElements()
}
reindexElements() {
let children = this.containerTarget.querySelectorAll('input')
for(let i = 0; i < children.length; i++) {
let child = children.item(i)
if (!this.isPositionInput(child)) {
continue
}
child.setAttribute('value', i)
}
}
closestWrapper(element) {
return element.closest(`.${this.childElementClass}`)
}
normalizeExistingChildren() {
let children = this.containerTarget.children
for(let i = 0; i < children.length; i++) {
let child = children.item(i)
if (this.isIdInput(child)) {
continue
}
child.classList.add(this.childElementClass)
if (!this.isIdInput(child.nextElementSibling)) {
continue
}
child.appendChild(child.nextElementSibling)
}
}
isIdInput(element) {
return this.isInputFor(element, 'id')
}
isPositionInput(element) {
return this.isInputFor(element, 'position')
}
isInputFor(element, attrName) {
if (!element || !attrName) {
return false
}
const name = element.getAttribute('name')
return element.tagName.toLowerCase() === "input" &&
name && name.endsWith(`[${attrName}]`)
}
addElement() {
let element = document.createElement('div')
var template = this.template
if (this.placeholder) {
template = template.replaceAll(`[${this.placeholder}]`, `[${(new Date).getTime()}]`)
}
element.innerHTML = template
if (!element.firstChild) {
element.remove()
return
}
element.firstChild.classList.add(this.childElementClass)
this.containerTarget.appendChild(element.firstChild)
element.remove()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment