Skip to content

Reworked JS helpers with APi #17 #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -271,12 +271,16 @@ In our `ConferenceForm` we can dynamically create/remove `Speaker` objects. To d
</div>

<h2>Speaker Details</h2>
<%= f.fields_for :speakers do |speaker_fields| %>
<%= render "speaker_fields", :f => speaker_fields %>
<% end %>

<%= render_association_template f, :speakers %>
<div class="speakers_list">
<%= f.fields_for :speakers do |speaker_fields| %>
<%= render "speaker_fields", :f => speaker_fields %>
<% end %>
</div>

<div class="links">
<%= link_to_add_association "Add a Speaker", f, :speakers %>
<%= link_to_add_association "Add a Speaker", f, :speakers, data: { "association-insertion-node" => ".speakers_list" } %>
</div>

<div class="actions">
Expand Down
136 changes: 136 additions & 0 deletions app/assets/javascripts/activeform.js
Original file line number Diff line number Diff line change
@@ -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);
61 changes: 0 additions & 61 deletions app/assets/javascripts/link_helpers.js

This file was deleted.

2 changes: 1 addition & 1 deletion lib/active_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
25 changes: 14 additions & 11 deletions lib/active_form/view_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand All @@ -21,35 +21,38 @@ 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)
method_name = :simple_fields_for
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
Expand Down
2 changes: 1 addition & 1 deletion lib/rails/generators/form/form_install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion test/dummy/app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@
//
//= require jquery
//= require jquery_ujs
//= require link_helpers
//= require activeform
//= require_tree .
19 changes: 7 additions & 12 deletions test/dummy/app/assets/javascripts/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
17 changes: 12 additions & 5 deletions test/dummy/app/views/assignments/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,22 @@
</div>

<h2>Tasks Details</h2>
<%= f.fields_for :tasks do |task_fields| %>
<%= render "task_fields", :f => task_fields %>
<% end %>

<%= render_association_template(f, :tasks) %>
<div class="task_list">
<%= f.fields_for :tasks do |task_fields| %>
<%= render "task_fields", :f => task_fields %>
<% end %>
</div>

<div class="links">
<%= link_to_add_association "Add a Task", f, :tasks %>
<%= link_to_add_association "Add a Task", f, :tasks, { data: {"association-insertion-node" => ".task_list"} } %>
</div>

<!-- $(".task_list").addFormField("tasks") -->
<!-- $(".task_list").removeFormField("tasks", 2) -->

<div class="actions">
<%= f.submit %>
</div>
<% end %>
<% end %>
2 changes: 1 addition & 1 deletion test/dummy/app/views/assignments/_task_fields.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
<%= f.text_field :name %>
<%= link_to_remove_association "Delete", f %>
</div>
</div>
</div>
9 changes: 6 additions & 3 deletions test/dummy/app/views/conferences/_speaker.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
</div>

<h2>Presentantions</h2>
<%= render_association_template f, :presentations %>
<div class="presentations_list">
<%= f.fields_for :presentations do |presentations_fields| %>
<%= render "presentation_fields", :f => presentations_fields %>
<%= render "presentation_fields", f: presentations_fields %>
<% end %>
</div>

<div class="links">
<%= link_to_add_association "Add a Presentation", f, :presentations %>
</div>
<%= link_to_add_association "Add a Presentation", f, :presentations, data: { "association-insertion-node" => ".presentations_list" } %>
</div>
Loading