Here is quick overview how I made dynamically creating and populating select fields, instead of using one select to display all levels. I am using it for selecting one category when creating a new user. gem awesome_nested_set
View code (new user)
<div id="user_category_select">
<span id="loading" style="display:none;">Loading...</span>
<%= f.select :user_category_id, category_children(@categories), { :include_blank => true } %>
</div>
Helper
def category_children(objects)
if(objects)
output = Array.new
objects.each do |o|
output << [o.name, o.id]
end
output
end
end
controller
def new
@categories = UserCategory.roots
end
def get_nested_categories
if(params[:id])
@categories = UserCategory.find(params[:id]).children
if(!@categories.empty?)
respond_to do |format|
format.js
end
else
render :nothing => true
end
else
render :nothing => true
end
end
Javascript
// get actual values from first select
var real_id = $('#user_category_select select').attr("id");
var real_name = $('#user_category_select select').attr("name");
$('#user_category_select select').live('change', function(){
remove_select_after($(this).next("select"));
if($(this).val()) {
manage_select_class($("#user_category_select select"), $(this), real_id, real_name);
$("#user_category_select #loading").show();
$.get("get_nested_categories",
{ id: $(this).val() }, function() { $("#user_category_select #loading").hide(); }, "script");
} else {
manage_select_class($("#user_category_select select"), $(this).prev("select"), real_id, real_name);
}
});
// remove all next selects to generate new
function remove_select_after(object) {
if(object.length) {
var next = object.next("select");
object.remove();
if(next.length) remove_select_after(next);
}
}
// remove all ids and names and add to current one
// since there is just one category value to send
function manage_select_class(remove, assign, id, name) {
remove.attr("name", ""); remove.attr("id", "");
assign.attr("name", name); assign.attr("id", id);
}
get_nested_categories.js
$("#user_category_select").append("<%= escape_javascript(select :not, :selected, category_children(@categories), { :include_blank => true }) %> ");
I am sure that someone could re-factor some code better, especially JS, so fell free to post it in comments
Thanks for the help, i was looking out for this solution.
ReplyDeleteBut could you pls elaborate more on the javascript code?
where exactly do we need to save the code you posted?
Aditya,
ReplyDeleteTell me what part of the JS you don't understand and i'll try to make it clearer. Did you build anything so far?
def category_children(objects)
ReplyDeleteobjects.map { |o| [o.label, o.id] } if (objects)
end