Scaffolding on Crack

Do you have a massive list of items in a scaffold-type arrangement (e.g. in a typo-style admin)? Here’s a way to quickly and easily filter and sort your big-assed list.

Note: this requires the Hash#to_sql function posted here a few weeks ago .. told you it was useful!

More after the jump..

First, we’re going to tackle filtering; if y’all want sorting or ajax, too, please leave some comments to motivate me to write the second half. Just so I know you care :)

Like any good web coder (har, har) let’s start with the view. We have articles, they’re organized into simple categories (Category model), written by authors (Author.find(:all) is stored in @authors)

<table>
<thead>
<tr>
<th>Post title</th>
<th><select name=’main_category’ onchange=’filtering();’ id=’filter_category’>
<option value=”>Main Category</option>
<%= options_from_collection_for_select(Category.find(:all), ‘permalink’, ‘category_name’, params[:category].to_i)%>
</select></th>
<th>
<select name=’author’ onchange=’filtering();’ id=’filter_author’>
<option value=”>Author</option>
<%= options_from_collection_for_select(Author.find(:all), ‘id’, ‘nickname’, params[:author].to_i )%>
</select></th>
</tr>
</thead>

Now, that’s the header.. it looks something like this:

Post title   Main Category   Monkeys   Elephants   Author   Joe   Fred
The giant monkey returns Monkeys Joe
Who’s the biggest, grayest mofo? Elephants Fred

Finally, some pagination:

<div class=”pagination”>

<% @pagination_params = {
:category => params[:category],
:author   => params[:author],
:action   => :list,
:controller => ‘article’
}
-%>
<%= pagination_links(
@content_pages, :window_size => 15,
:link_to_current_page => false,
:params => @pagination_params
)%>

</div>

Now, you may notice there’s some javascript in those Select boxes: onchange=’filtering();’. Here’s that function.

<script type=’text/javascript’>
function filtering() {
var url = “/article/list/?filter”
var category = $(‘filter_category’);
var author = $(‘filter_author’);

if (category.selectedIndex > 0) url += ‘&category=’ + category.options[category.selectedIndex].value;
if (author.selectedIndex > 0)   url += ‘&author=’ + author.options[author.selectedIndex].value;
window.location.href = url;
}
</script>

Hopefully that’s fairly self-explanatory; it builds a query string from the values of the two select boxes. Next: onto the controller.

class ArticleController < ApplicationController
def list
# create a conditions hash from our params, if they exist
@conditions[:main_category_id] = Category.find_by_permalink(params[:category]).id if params[:category] and !params[:category].empty?
@conditions[:user_id] = params[:author] if params[:author] and !params[:author].empty?

@content_pages = Paginator.new self, Article.count, 10, @params[‘page’].to_i
@contents = Article.find(:all, :conditions => @conditions.to_sql, :order => ‘published_at desc’, :offset => @params[‘page’].to_i, :limit => 10)

@authors = Author.find_by_sql(“SELECT DISTINCT(content.user_id), users.* FROM #{Article.table_name} LEFT JOIN users ON content.user_id = users.id WHERE content.type = ‘Article’ ORDER BY users.nickname ASC”)
# this is our fairly specific sql, yours is probably much different.

@categories = Category.find(:all)
@authors = Author.find(:all)
end

end

Voila! We now have dropdown filtering.