ChrisCodes.com

How to specify columns to appear in Ajax Scaffolding generated views

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

Feed Icon

Copyright © 2009 Chris Martin