Commit a941d776791c41ab9ebf7b04f32326e60dbcb4d5
1 parent
2ab6a694
Exists in
master
and in
1 other branch
Add JS to allow adding/destroying nested attributes
Showing
3 changed files
with
57 additions
and
3 deletions
Show diff stats
app/models/project.rb
| @@ -15,7 +15,7 @@ class Project | @@ -15,7 +15,7 @@ class Project | ||
| 15 | validates_presence_of :name, :api_key | 15 | validates_presence_of :name, :api_key |
| 16 | validates_uniqueness_of :name, :api_key, :allow_blank => true | 16 | validates_uniqueness_of :name, :api_key, :allow_blank => true |
| 17 | 17 | ||
| 18 | - accepts_nested_attributes_for :watchers, | 18 | + accepts_nested_attributes_for :watchers, :allow_destroy => true, |
| 19 | :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } } | 19 | :reject_if => proc { |attrs| attrs.all? { |k, v| v.blank? } } |
| 20 | 20 | ||
| 21 | def self.find_by_api_key!(key) | 21 | def self.find_by_api_key!(key) |
app/views/projects/_fields.html.haml
| @@ -2,9 +2,9 @@ | @@ -2,9 +2,9 @@ | ||
| 2 | = f.label :name | 2 | = f.label :name |
| 3 | = f.text_field :name | 3 | = f.text_field :name |
| 4 | 4 | ||
| 5 | -%fieldset | 5 | +%fieldset.nested-wrapper |
| 6 | %legend Watchers | 6 | %legend Watchers |
| 7 | - f.fields_for :watchers do |w| | 7 | - f.fields_for :watchers do |w| |
| 8 | - %div | 8 | + %div.nested |
| 9 | = w.label :email | 9 | = w.label :email |
| 10 | = w.text_field :email | 10 | = w.text_field :email |
| 11 | \ No newline at end of file | 11 | \ No newline at end of file |
public/javascripts/application.js
| @@ -0,0 +1,54 @@ | @@ -0,0 +1,54 @@ | ||
| 1 | +$(function(){ | ||
| 2 | + activateNestedForms(); | ||
| 3 | +}); | ||
| 4 | + | ||
| 5 | +function activateNestedForms() { | ||
| 6 | + $('.nested-wrapper').each(function(){ | ||
| 7 | + var wrapper = $(this); | ||
| 8 | + | ||
| 9 | + makeNestedItemsDestroyable(wrapper); | ||
| 10 | + | ||
| 11 | + var addLink = $('<a/>').text('add another').addClass('add-nested'); | ||
| 12 | + addLink.click(appendNestedItem); | ||
| 13 | + wrapper.append(addLink); | ||
| 14 | + }); | ||
| 15 | + $('.nested a.remove-nested').live('click',removeNestedItem); | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +function makeNestedItemsDestroyable(wrapper) { | ||
| 19 | + wrapper.find('.nested').each(function(){ | ||
| 20 | + var nestedItem = $(this); | ||
| 21 | + var destroyLink = $('<a/>').text('remove').addClass('remove-nested'); | ||
| 22 | + destroyLink.css('float','right'); | ||
| 23 | + nestedItem.find('label').first().prepend(destroyLink); | ||
| 24 | + }) | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +function appendNestedItem() { | ||
| 28 | + var addLink = $(this); | ||
| 29 | + var nestedItem = addLink.parent().find('.nested').first().clone(); | ||
| 30 | + nestedItem.find('input, select').each(function(){ | ||
| 31 | + var input = $(this); | ||
| 32 | + var timestamp = new Date(); | ||
| 33 | + timestamp = timestamp.valueOf(); | ||
| 34 | + input.attr('id', input.attr('id').replace(/([_\[])\d+([\]_])/,'$1'+timestamp+'$2')); | ||
| 35 | + input.attr('name', input.attr('name').replace(/([_\[])\d+([\]_])/,'$1'+timestamp+'$2')); | ||
| 36 | + input.val(''); | ||
| 37 | + }); | ||
| 38 | + addLink.before(nestedItem); | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +function removeNestedItem() { | ||
| 42 | + var destroyLink = $(this); | ||
| 43 | + var nestedItem = destroyLink.closest('.nested'); | ||
| 44 | + var inputNameExample = nestedItem.find('input').first().attr('name'); | ||
| 45 | + var idFieldName = inputNameExample.replace(/\[[^\]]*\]$/,'[id]'); | ||
| 46 | + if($("input[name='"+idFieldName+"']").length) { | ||
| 47 | + var destroyFlagName = inputNameExample.replace(/\[[^\]]*\]$/,'[_destroy]') | ||
| 48 | + var destroyFlag = $('<input/>').attr('name',destroyFlagName).attr('type','hidden').val('true'); | ||
| 49 | + $("input[name='"+idFieldName+"']").after(destroyFlag); | ||
| 50 | + nestedItem.hide(); | ||
| 51 | + } else { | ||
| 52 | + nestedItem.remove(); | ||
| 53 | + } | ||
| 54 | +} | ||
| 0 | \ No newline at end of file | 55 | \ No newline at end of file |