diff --git a/README.md b/README.md
index 958963d..668f044 100644
--- a/README.md
+++ b/README.md
@@ -242,7 +242,7 @@ Active Form comes with two helpers to deal with this functionality:
1. `link_to_add_association` will display a link that renders fields to create a new object.
2. `link_to_remove_association` will display a link to remove a existing/dynamic object.
-In order to use it you have to insert this line: `//= require link_helpers` to your `app/assets/javascript/application.js` file.
+In order to use it you have to insert this line: `//= require activeform` to your `app/assets/javascript/application.js` file.
In our `ConferenceForm` we can dynamically create/remove `Speaker` objects. To do that we would write in the `app/views/conferences/_form.html.erb` partial:
@@ -271,12 +271,16 @@ In our `ConferenceForm` we can dynamically create/remove `Speaker` objects. To d
Speaker Details
- <%= f.fields_for :speakers do |speaker_fields| %>
- <%= render "speaker_fields", :f => speaker_fields %>
- <% end %>
+
+ <%= render_association_template f, :speakers %>
+
+ <%= f.fields_for :speakers do |speaker_fields| %>
+ <%= render "speaker_fields", :f => speaker_fields %>
+ <% end %>
+
- <%= link_to_add_association "Add a Speaker", f, :speakers %>
+ <%= link_to_add_association "Add a Speaker", f, :speakers, data: { "association-insertion-node" => ".speakers_list" } %>
diff --git a/app/assets/javascripts/activeform.js b/app/assets/javascripts/activeform.js
new file mode 100644
index 0000000..3f476e6
--- /dev/null
+++ b/app/assets/javascripts/activeform.js
@@ -0,0 +1,136 @@
+(function($) {
+ // Form API, draft version:
+ // $(".task_list").addFormField("tasks")
+ // $(".task_list").removeFormField("tasks", 2) // destroy second field
+
+ var activeFormHelpers = {
+ generateResourceId: function() {
+ return new Date().getTime();
+ }
+ };
+
+ $.fn.removeFormField = function(nodeToDeleteOrIndex, options) {
+ if(options == undefined) {
+ options = {}
+ }
+
+ var nodeToDelete;
+
+ if(nodeToDeleteOrIndex instanceof jQuery) {
+ nodeToDelete = nodeToDeleteOrIndex;
+ } else {
+ var wrapperClass = this.data('wrapper-class') || 'nested-fields';
+ console.log(wrapperClass)
+ nodeToDelete = this.find("." + wrapperClass).filter(function(i, b) {
+ return $(b).css('display') != 'none';
+ }).eq(nodeToDeleteOrIndex);
+ }
+
+ this.trigger('before-remove', [nodeToDelete]);
+
+ var timeout = this.data('remove-timeout') || 0;
+
+ var input = nodeToDelete.find("input").filter(function(i, t) {
+ return false
+ })
+
+ var isDynamic = nodeToDelete.hasClass("dynamic");
+
+ var context = this;
+ setTimeout(function() {
+ if (isDynamic) {
+ nodeToDelete.remove();
+ } else {
+ nodeToDelete.find("input[type=hidden]").val("1");
+ nodeToDelete.hide();
+ }
+ context.trigger('after-remove', [nodeToDelete]);
+ }, timeout);
+
+ return this
+ };
+
+ $.fn.addFormField = function(assoc, options) {
+ if(options == undefined) {
+ options = {}
+ }
+ var templateSelector = "." + assoc + "_template";
+
+ var templateContainer;
+ $(this).parents().each(function(i, el){
+ var found = $(el).find(templateSelector)
+ if(found.length > 0) {
+ templateContainer = found.eq(0)
+ return false;
+ }
+ })
+
+ var newId = activeFormHelpers.generateResourceId();
+ var regex = new RegExp("new_" + assoc, "g");
+ var content = templateContainer.html().replace(regex, newId);
+ var contentNode = $(content);
+
+ contentNode.addClass("dynamic")
+
+ if(!options.insertionMethod) {
+ options.insertionMethod = 'append';
+ }
+
+ this.trigger('before-insert', [contentNode]);
+
+ var addedContent = this[options.insertionMethod](contentNode);
+
+ this.trigger('after-insert', [contentNode]);
+
+ return this;
+ };
+
+ $(document).on('click', '.add_fields', function(e) {
+ e.preventDefault();
+
+ var $link = $(this);
+ var form = $link.parents("form").eq(0);
+ var assoc = $link.data('association');
+ var insertionMethod = $link.data('association-insertion-method') || $link.data('association-insertion-position');
+ var insertionNode = $link.data('association-insertion-node');
+ var insertionTraversal = $link.data('association-insertion-traversal');
+
+ if (insertionNode){
+ if (insertionTraversal){
+ insertionNode = $link[insertionTraversal](insertionNode);
+ } else {
+ if(insertionNode == "this") {
+ insertionNode = $(this)
+ } else {
+ $(this).parents().each(function(i, el){
+ var found = $(el).find(insertionNode)
+ if(found.length > 0) {
+ insertionNode = found.eq(0)
+ return false;
+ }
+ })
+ }
+ }
+ } else {
+ insertionNode = $link.parent();
+ }
+
+ insertionNode.addFormField(assoc, {
+ insertionMethod: insertionMethod,
+ insertionTraversal: insertionTraversal,
+ })
+ });
+
+ $(document).on('click', '.remove_fields, .remove_fields', function(e) {
+ e.preventDefault();
+
+ var $link = $(this);
+ var wrapperClass = $link.data('wrapper-class') || 'nested-fields';
+ var nodeToDelete = $link.closest('.' + wrapperClass);
+
+ var triggerNode = nodeToDelete.parent();
+
+ triggerNode.removeFormField(nodeToDelete)
+ });
+
+})(jQuery);
diff --git a/app/assets/javascripts/link_helpers.js b/app/assets/javascripts/link_helpers.js
deleted file mode 100644
index 950eda4..0000000
--- a/app/assets/javascripts/link_helpers.js
+++ /dev/null
@@ -1,61 +0,0 @@
-(function($) {
-
- var createNewResourceID = function() {
- return new Date().getTime();
- }
-
- $(document).on('click', '.add_fields', function(e) {
- e.preventDefault();
-
- var $link = $(this);
- var assoc = $link.data('association');
- var content = $link.data('association-insertion-template');
- var insertionMethod = $link.data('association-insertion-method') || $link.data('association-insertion-position') || 'before';
- var insertionNode = $link.data('association-insertion-node');
- var insertionTraversal = $link.data('association-insertion-traversal');
- var newId = createNewResourceID();
- var regex = new RegExp("new_" + assoc, "g");
- var newContent = content.replace(regex, newId);
-
- if (insertionNode){
- if (insertionTraversal){
- insertionNode = $link[insertionTraversal](insertionNode);
- } else {
- insertionNode = insertionNode == "this" ? $link : $(insertionNode);
- }
- } else {
- insertionNode = $link.parent();
- }
-
- var contentNode = $(newContent);
- insertionNode.trigger('before-insert', [contentNode]);
-
- var addedContent = insertionNode[insertionMethod](contentNode);
-
- insertionNode.trigger('after-insert', [contentNode]);
- });
-
- $(document).on('click', '.remove_fields.dynamic, .remove_fields.existing', function(e) {
- e.preventDefault();
-
- var $link = $(this);
- var wrapperClass = $link.data('wrapper-class') || 'nested-fields';
- var nodeToDelete = $link.closest('.' + wrapperClass);
- var triggerNode = nodeToDelete.parent();
-
- triggerNode.trigger('before-remove', [nodeToDelete]);
-
- var timeout = triggerNode.data('remove-timeout') || 0;
-
- setTimeout(function() {
- if ($link.hasClass('dynamic')) {
- nodeToDelete.remove();
- } else {
- $link.prev("input[type=hidden]").val("1");
- nodeToDelete.hide();
- }
- triggerNode.trigger('after-remove', [nodeToDelete]);
- }, timeout);
- });
-
-})(jQuery);
diff --git a/lib/active_form.rb b/lib/active_form.rb
index 069fed5..8b8b985 100644
--- a/lib/active_form.rb
+++ b/lib/active_form.rb
@@ -10,7 +10,7 @@ class Engine < ::Rails::Engine
config.before_initialize do
if config.action_view.javascript_expansions
- config.action_view.javascript_expansions[:link_helpers] = %w(link_helpers)
+ config.action_view.javascript_expansions[:activeform] = %w(activeform)
end
end
diff --git a/lib/active_form/view_helpers.rb b/lib/active_form/view_helpers.rb
index a207b1c..72b1b86 100644
--- a/lib/active_form/view_helpers.rb
+++ b/lib/active_form/view_helpers.rb
@@ -7,7 +7,7 @@ def link_to_remove_association(name, f, html_options={})
is_existing = f.object.persisted?
classes << (is_existing ? 'existing' : 'dynamic')
-
+
wrapper_class = html_options.delete(:wrapper_class)
html_options[:class] = [html_options[:class], classes.join(' ')].compact.join(' ')
html_options[:'data-wrapper-class'] = wrapper_class if wrapper_class.present?
@@ -21,7 +21,7 @@ def link_to_remove_association(name, f, html_options={})
def render_association(association, f, new_object, render_options={}, custom_partial=nil)
partial = get_partial_path(custom_partial, association)
-
+
if f.respond_to?(:semantic_fields_for)
method_name = :semantic_fields_for
elsif f.respond_to?(:simple_fields_for)
@@ -29,27 +29,30 @@ def render_association(association, f, new_object, render_options={}, custom_par
else
method_name = :fields_for
end
-
+
f.send(method_name, association, new_object, {:child_index => "new_#{association}"}.merge(render_options)) do |builder|
render(partial: partial, locals: {:f => builder})
end
end
def link_to_add_association(name, f, association, html_options={})
- render_options = html_options.delete(:render_options)
- render_options ||= {}
- override_partial = html_options.delete(:partial)
-
html_options[:class] = [html_options[:class], "add_fields"].compact.join(' ')
html_options[:'data-association'] = association.to_s
+ link_to(name, '#', html_options)
+ end
+
+ def render_association_template(f, association, html_options={})
+ render_options = html_options.delete(:render_options)
+ render_options ||= {}
+ override_partial = html_options.delete(:partial)
new_object = create_object(f, association)
- html_options[:'data-association-insertion-template'] = CGI.escapeHTML(render_association(association, f, new_object, render_options, override_partial).to_str).html_safe
-
- link_to(name, '#', html_options)
+ content_tag :template, class: "association_template #{association}_template" do
+ render_association(association, f, new_object, render_options, override_partial)
+ end
end
-
+
def create_object(f, association)
f.object.get_model(association)
end
diff --git a/lib/rails/generators/form/form_install_generator.rb b/lib/rails/generators/form/form_install_generator.rb
index 8b3b72e..b39c358 100644
--- a/lib/rails/generators/form/form_install_generator.rb
+++ b/lib/rails/generators/form/form_install_generator.rb
@@ -20,7 +20,7 @@ def create_forms_test_directory
end
def include_js_file
- insert_into_file "app/assets/javascripts/application.js", "//= require link_helpers", :before => "//= require_tree ."
+ insert_into_file "app/assets/javascripts/application.js", "//= require activeform", :before => "//= require_tree ."
end
end
end
diff --git a/test/dummy/app/assets/javascripts/application.js b/test/dummy/app/assets/javascripts/application.js
index 43ef054..d4149cf 100644
--- a/test/dummy/app/assets/javascripts/application.js
+++ b/test/dummy/app/assets/javascripts/application.js
@@ -12,5 +12,5 @@
//
//= require jquery
//= require jquery_ujs
-//= require link_helpers
+//= require activeform
//= require_tree .
diff --git a/test/dummy/app/assets/javascripts/projects.js b/test/dummy/app/assets/javascripts/projects.js
index 687ad17..96d27e2 100644
--- a/test/dummy/app/assets/javascripts/projects.js
+++ b/test/dummy/app/assets/javascripts/projects.js
@@ -23,25 +23,20 @@ $(document).ready(function() {
$(".project-tag-fields a.add_fields").
data("association-insertion-position", 'before').
data("association-insertion-node", 'this');
- $('.project-tag-fields').bind('after-insert',
- function() {
- $(this).children("#tag_from_list").remove();
- $(this).children("a.add_fields").hide();
- });
});
+ $("body").on('after-insert', '.project-tag-fields',
+ function() {
+ $(this).children("#tag_from_list").remove();
+ $(this).children("a.add_fields").hide();
+ });
+
$('#tasks').bind('before-insert', function(e,task_to_be_added) {
task_to_be_added.fadeIn('slow');
});
- $('#tasks').bind('after-insert', function(e, added_task) {
- //added_task.css("background","red");
- });
-
$('#tasks').bind('before-remove', function(e, task) {
$(this).data('remove-timeout', 1000);
task.fadeOut('slow');
})
-
- $('body').tabs();
-});
\ No newline at end of file
+});
diff --git a/test/dummy/app/views/assignments/_form.html.erb b/test/dummy/app/views/assignments/_form.html.erb
index b7c816f..e3f95ae 100644
--- a/test/dummy/app/views/assignments/_form.html.erb
+++ b/test/dummy/app/views/assignments/_form.html.erb
@@ -17,15 +17,22 @@
Tasks Details
- <%= f.fields_for :tasks do |task_fields| %>
- <%= render "task_fields", :f => task_fields %>
- <% end %>
+
+ <%= render_association_template(f, :tasks) %>
+
+ <%= f.fields_for :tasks do |task_fields| %>
+ <%= render "task_fields", :f => task_fields %>
+ <% end %>
+
- <%= link_to_add_association "Add a Task", f, :tasks %>
+ <%= link_to_add_association "Add a Task", f, :tasks, { data: {"association-insertion-node" => ".task_list"} } %>
+
+
+
<%= f.submit %>
-<% end %>
\ No newline at end of file
+<% end %>
diff --git a/test/dummy/app/views/assignments/_task_fields.html.erb b/test/dummy/app/views/assignments/_task_fields.html.erb
index e5022b3..80335e3 100644
--- a/test/dummy/app/views/assignments/_task_fields.html.erb
+++ b/test/dummy/app/views/assignments/_task_fields.html.erb
@@ -4,4 +4,4 @@
<%= f.text_field :name %>
<%= link_to_remove_association "Delete", f %>
-
\ No newline at end of file
+
diff --git a/test/dummy/app/views/conferences/_speaker.html.erb b/test/dummy/app/views/conferences/_speaker.html.erb
index 440d996..5969fbf 100644
--- a/test/dummy/app/views/conferences/_speaker.html.erb
+++ b/test/dummy/app/views/conferences/_speaker.html.erb
@@ -9,10 +9,13 @@
Presentantions
+<%= render_association_template f, :presentations %>
+
<%= f.fields_for :presentations do |presentations_fields| %>
- <%= render "presentation_fields", :f => presentations_fields %>
+ <%= render "presentation_fields", f: presentations_fields %>
<% end %>
+
- <%= link_to_add_association "Add a Presentation", f, :presentations %>
-
\ No newline at end of file
+ <%= link_to_add_association "Add a Presentation", f, :presentations, data: { "association-insertion-node" => ".presentations_list" } %>
+
diff --git a/test/dummy/app/views/projects/_form.html.erb b/test/dummy/app/views/projects/_form.html.erb
index 27f3649..34b4b35 100644
--- a/test/dummy/app/views/projects/_form.html.erb
+++ b/test/dummy/app/views/projects/_form.html.erb
@@ -1,5 +1,5 @@
<%= simple_form_for @project_form, wrapper: 'inline' do |f| %>
- <%= f.input :name, :hint => 'The title of your project' %>
+ <%= f.input :name, hint: 'The title of your project' %>