Last active
May 4, 2020 11:36
-
-
Save jaz303/a415342b6e3eb06b3654b0f9d3e08e06 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Simple jQuery helper for coordinating a dynamic list of nested | |
// form items, compatible with Rails' nested attributes. | |
// | |
// Supports deletion of saved items. | |
// | |
// HTML for existing list items should be pre-generated by the | |
// template (see template.html.erb). It is assumed the last | |
// item will be an empty "template" row; this can be added | |
// in the controller with `@model.association.new` | |
// | |
// Hook classes: | |
// x-empty: element to show when list is empty | |
// x-add: button to click to add an item | |
// x-list: container for list items | |
// x-item: outermost wrapper element for each item | |
// x-delete: delete button (must be inside .x-item) | |
// | |
function repeater($root) { | |
const RepeaterIndex = /^(\w+\[\w+\])\[(\d+)\]/; | |
const ui = { | |
root: $root, | |
empty: $root.find('.x-empty'), | |
add: $root.find('.x-add'), | |
list: $root.find('.x-list'), | |
items: $root.find('.x-item'), | |
}; | |
const template = ui.items.last().clone(); | |
let nextIndex = ui.items.length; | |
let count = ui.items.length; | |
countChanged(0); | |
ui.add.on('click', function(evt) { | |
generateNewItem().appendTo(ui.list); | |
countChanged(1); | |
}); | |
ui.root.on('click', '.x-delete', function() { | |
const item = $(this).closest('.x-item'); | |
item.append(makeDeletedField(item)).hide(); | |
countChanged(-1); | |
}); | |
function makeDeletedField(item) { | |
const named = item.find('[name]'); | |
for (let i = 0; i < named.length; ++i) { | |
if (named[i].name.match(RepeaterIndex)) { | |
return `<input type="hidden" name="${RegExp.$1 + '[' + RegExp.$2 + ']'}[_destroy]" value="1"/>`; | |
} | |
} | |
throw new Error("couldn't make deleted field"); | |
} | |
function generateNewItem() { | |
const index = nextIndex++; | |
const item = template.clone(); | |
item.find('[name]').each(function() { | |
this.name = this.name.replace(RepeaterIndex, (_, baseName) => baseName + "[" + index + "]"); | |
}); | |
return item; | |
} | |
function countChanged(delta) { | |
count += delta; | |
ui.empty[count < 1 ? 'show' : 'hide'](); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="field"> | |
<label class="label">Files</label> | |
<div class="field-body repeater"> | |
<div class='x-empty' style='display:none'>Select some files to upload!</div> | |
<button class='x-add' type='button'>Add</button> | |
<div class='x-list'> | |
<%= f.fields_for :files do |b| %> | |
<div class='x-item'> | |
<%= b.select :file_type, UploadFile::FILE_TYPES %> | |
<%= b.file_field :file_data %> | |
<button type='button' class='x-delete'>Delete</button> | |
</div> | |
<% end %> | |
</div> | |
</div> | |
</div> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment