I've been playing with the Ajax Scaffolding gem for rails. Very cool stuff. It struck me funny when I read there was no magic way to say what columns appear, other than hard coding them in the view. Originally, I felt the same way when I first looked at generated views in general. But starting to hard code stuff in those beautifully useful views was getting me. I quickly remembered that Ruby is full of Magic, and figured out a way.
In this example, my database table name is sites. So in typical rails fashion, my model is site.rb, my controller is sites_controller.rb, and it's views are in app/views/sites/
The Ajax Scaffold generated view that displays the fields is named app/views/sites/_site.rhtml
The component action in the AjaxScaffold generated controller looks something like this:
def component
update_params :default_scaffold_id => "site", :default_sort => nil, :default_sort_direction => "asc"
@sort_sql = Site.scaffold_columns_hash[current_sort(params)].sort_sql rescue nil
@sort_by = @sort_sql.nil? ? "#{Site.table_name}.#{Site.primary_key} asc" : @sort_sql + " " + current_sort_direction(params)
@paginator, @sites = paginate(:sites, :order => @sort_by, :per_page => default_per_page)
render :action => "component", :layout => false
end
So to setup which columns should be allowed to show up is a matter of adding one line defining an array of column names.
def component
update_params :default_scaffold_id => "site", :default_sort => nil, :default_sort_direction => "asc"
@sort_sql = Site.scaffold_columns_hash[current_sort(params)].sort_sql rescue nil
@sort_by = @sort_sql.nil? ? "#{Site.table_name}.#{Site.primary_key} asc" : @sort_sql + " " + current_sort_direction(params)
@paginator, @sites = paginate(:sites, :order => @sort_by, :per_page => default_per_page)
@allowed_columns = ["Name", "Url", "Code", "Lang"] render :action => "component", :layout => false end
Then in app/views/sites/_column_headings.rhtml we add a simple if statement to see if the column is supposed to display.
<%
for scaffold_column in scaffold_columns
if @allowed_columns.include? scaffold_column.label
column_sort_direction = column_sort_direction(scaffold_column.name, params)
sort_params = params.merge(:controller => '/sites', :action => 'component_update', :sort => scaffold_column.name, :sort_direction => column_sort_direction, :page => 1)
column_header_id = scaffold_column_header_id(sort_params.merge(:column_name => scaffold_column.name))
%>
<th id="<%= column_header_id %>" <%= "class=\\"sorted #{current_sort_direction(params)}\\"" if scaffold_column.name == current_sort(params) %>>
<% if scaffold_column.sortable? %>
<%= link_to_remote scaffold_column.label,
{ :url => sort_params,
:loading => "Element.addClassName('#{column_header_id}','loading');",
:update => scaffold_content_id(sort_params) },
{ :href => url_for(sort_params) } %>
<% else %>
<p><%= scaffold_column.label %></p>
<% end %>
</th>
<%
end
end
%>
<th></th>
The same needs to be done in app/views/sites/_site.rhtml
<% # The following is used when the browser doesn't have javascript enabled %>
<% classAttr = cycle("", "class=\\"even\\"") %>
<% @options = params.merge(:controller => '/sites', :action => "view", :id => site.send("#{Site.primary_key}")) %>
<tr <%= classAttr %> id="<%= element_row_id(@options) %>" <%= "style=\\"display: none;\\"" if hidden %>>
<%
for scaffold_column in scaffold_columns
if @allowed_columns.include? scaffold_column.label
%>
<% column_value = eval(scaffold_column.eval) rescue nil %>
<td class="<%= column_class(scaffold_column.name, column_value, current_sort(params)) %>" >
<%= format_column(column_value) %>
</td>
<%
end
end
%>
<td class="actions">
<table cellpadding="0" cellspacing="0">
<tr>
<td class="indicator-container">
<%= loading_indicator_tag(@options) %>
</td>
<td>
<% edit_options = @options.merge(:action => 'edit') %>
<%= link_to_remote "Edit",
{ :url => edit_options,
:loading => "Element.show('#{loading_indicator_id(@options)}');" },
{ :href => url_for(edit_options) } %>
</td>
<td>
<% delete_options = @options.merge(:action => 'destroy') %>
<%= link_to_remote "Delete",
{ :url => delete_options,
:confirm => 'Are you sure?',
:loading => "Element.show('#{loading_indicator_id(@options)}');" },
{ :href => url_for( delete_options ) } %>
</td>
</tr>
</table>
</td>
</tr>
That's it, now only the columns specified will appear. I'm sure there are other ways to acheive this, but this seems easy enough.
Chris keyed this in on: 2006-05-27
Filed in: Ajax, Database, Rails, Ruby