DEV Community

Tyler Scott Williams
Tyler Scott Williams

Posted on

A Nifty Use Case for Rails Virtual Attributes

I work on a Rails project uses models to represent some front end components, which gives us access to clever tricks, including using virtual attributes for custom styling.

The base component class

All of these components inherit from a Component model that looks like this:

class Component < ApplicationRecord
  def css_class_list
    class_string = "#{self.class}"
    self.style_list.each { |style| class_string << (" " + style.to_s) if self.send(style) }
    class_string
  end
end
Enter fullscreen mode Exit fullscreen mode

The css_class_list method spits out a class list for use in our ERB templates, like so:

<div class="<%= component.css_class_list %>"></div>
Enter fullscreen mode Exit fullscreen mode

If we want to build out some EventComponent that inherits from the Component class, its rendered template would come out like this:

<div class="EventComponent"></div>
Enter fullscreen mode Exit fullscreen mode

Customize the event component styles

Let's say we want to add some different flavors to the EventComponent. We can add additional CSS classes using the style_list method:

class EventComponent < Component
  belongs_to :page

  def style_list
    [
      :description_enabled
    ]
  end
end
Enter fullscreen mode Exit fullscreen mode

The parent class, Component, grabs style_list and sends each symbol to the instance of the EventComponent. If it returns true, it adds the symbol to the final class list.

In this case, if we have an EventComponent with description_enabled, the rendered template would be:

<div class="EventComponent description_enabled"></div>
Enter fullscreen mode Exit fullscreen mode

Generate CSS classes with virtual attributes

This works well for model attributes that are boolean values, but what if we want to style based on a non-boolean attribute? This is where virtual attributes really shine.

Here's a version of EventComponent that we can style based on the number of attendees:

class EventComponent < Component
  belongs_to :page

  def over_capacity # This is the virtual attribute
    rsvp_count > 1000
  end

  def style_list
    [
      :description_enabled
      :over_capacity
    ]
  end
end
Enter fullscreen mode Exit fullscreen mode

Now if we render out an EventComponent with more than 1000 RSVPs, the output will look like:

<div class="EventComponent description_enabled over_capacity"></div>
Enter fullscreen mode Exit fullscreen mode

And we can go on to target .EventComponent, .description_enabled, and .over_capacity with our CSS!

Discussion (2)

Collapse
drbragg profile image
Drew Bragg

Interesting.

Out of curiosity, why are you using models to represent frontend components instead of View Components?

Collapse
ogdenstudios profile image
Tyler Scott Williams Author

The project is older than view components, so we never really got around to adding them to the project. Around the time we finished the first big deployment, I read an article about view components and was like "Hey! That's exactly what we're doing with our component system!" But we already have all our stuff in place and it's working well.