Skip to main content

Conditional Features in Tabular Layouts

08-31-15 Mike Yockey

For complex pages, it can be difficult to manage displaying some things conditionally. Mike shares how to be expressive today so that future you can more easily understand what’s going on.

Tabular layouts can be difficult to work with, especially when some display elements are conditional. Outside of email templates, tables are largely eschewed in favor of logical divisions or more semantic HTML, but what if the data you have for display is actually tabular data? I want to show you a technique I wrote for dealing with conditional columns in HTML tables.

I’ve used the idea of a feature flag before to toggle the availability of new development in an application. For these conditionals, I’ve copied much of that same design down to a smaller scale, including the name “Feature.” A feature may be initialized during a request and queried when rendering a template.

widget.html.erb:

<table>
    <thead>
        <th>Foo</th>
        <% if feature_enabled?(:premium) %>
            <th>Premium</th>
        <% end %>
    </thead>
    <tbody>
        <tr>
            <td><%= @widget.foo %></td>
            <% if feature_enabled?(:premium) %>
                <td><%= @widget.premium %></td>
            <% end %>
        </tr>
    </tbody>
</table>

widget_controller.rb:

class WidgetController
    def show
        add_feature(:premium)
        @widget = Widget.find(widget_params)
    end
end

With these template features though, there’s an extra layer of indirection. Rather than a simple on/off switch, I need to inspect the properties of another model before the feature may be displayed. While that part is delegated to another class, the important piece here is how we interact with those classes during the request. Two new controller methods, add_feature and feature_enabled?, let us initialize features and see if they’re available to the request.

premium_feature.rb:

class PremiumFeature
    attr_accessor :controller
    def initialize(controller)
        @controller = controller
    end
    def enabled?
        controller.current_user.premium?
    end
end

widget_controller.rb

class WidgetController
  # Further down the controller class
  def add_feature(name)
      feature = "#{name}_feature".camelize.constantize.new(self)
      if feature.respond_to?(:enabled?)
          features[name] = feature
      end
  rescue
      fail 'Feature does not exist'
  end
  
  def feature_enabled?(name)
      features.fetch(name, NeverEnabledFeature.new).enabled?
  end
  helper_method :feature_enabled?
end

When I called add_feature(:premium), a new instance of PremiumFeature was initialized and added to the features hash by name. When I called feature_enabled?(:premium), it retrieved that instance and called its enabled? method. As an added bonus, if we query for a feature that doesn’t exist, Ruby’s Hash#fetch method lets us define a default return value, which in this case is a class whose enabled? method always returns false. Note that the features hash itself isn’t shown in the examples. For this application, I used Rails’ HashWithIndifferentAccess so that features could be queried by symbol or string. You could use a simple hash instance if you wished to be more strict.

An idea to take this utility further might be to allow unimplemented features by name. Extend the idea of the default NeverEnabledFeature and add an AlwaysEnabledFeature when adding a feature by name that does not exist. You could also move feature initialization into your app’s configuration if usage were more widespread.

There really are few options when conditionally rendering columns in an HTML table, and thankfully this should be a rare case. Without any way to disable entire columns, an easy Boolean helps to keep templates readable and thus maintainable. There’s little doubt that a future programmer, or your future self, will appreciate you having taken the time to be expressive in such a corner case as this.

Related Content

Want to talk about how we can work together?

Katie can help

A portrait of Vice President of Business Development, Katie Jennings.

Katie Jennings

Vice President of Business Development