A useful pattern for managing front-end patterns in a Rails app
Most large sites will want to abstract common front-end patterns into components, which lets them be reused in needed contexts and create a single source of truth. One way to approach this is with helpers. This will walk through the basic steps to set one up.
1) Create an HTML template
Let’s say we want to make a dropdown menu component. It’ll need text for the trigger, two unique IDs, and a list of links with text to fill up the menu. It should let you set optional classes and properties the wrapper so its more flexible.
First we need the HTML. Let’s use the Material Web Components HTML as an example.
That’s the easy part. But the static info here must be replaced with variables we’ll want to change for each use. This can be done with normal
.erb syntax. They can have whatever names and structures you want, but you’ll need to remember them for later.
2) Create a Helper to Pull the Template
We have the template, now we need some way to pull it when needed. We could reference the template directly, but using a helper lets you use more logic around constructing it so it’s much easier and DRYer.
In the helper folder let’s make a
components_helper.rb file and set up the basics.
Let’s focus on the
dropdown_component method. First is to set up the render and list all the values it will need. These will be the variables we referenced in the template. These must be passed into the method as arguments, and then assigned to their respective values in the template.
This is a straight 1-1 rendering of the variables, but using it as a helper lets us simplify it. For instance, both IDs are going to be random IDs, so we can default them to random strings by default. The method to create this random string can be defined elsewhere in the helper if you don’t already have a method like this.
Now when we call the helper for the component, we only need the first two arguments. So we could call it like so.
3) Adding Optional Classes and Properties
For the wrapper, we may need to add additional classes and properties based on the context. There’s a few steps to getting this functionality.
3a) Add an Options Arguments
Obviously we need an argument to pass the options into. We’ll call it
wrapper_options and set it to
nil by default since it’s optional.
3b) Create a Method for Preserving Default Classes
One obstacle you’ll hit is that the wrapper needs a default class,
"mdc-menu-anchor". We want this class to always be there, and not be over-written by new classes. We can create a method that will take the classes from
wrapper_options and merge them into a default. We’ll call it
class_merge, and include it in the helper module.
This accepts our
wrapper_options object and a set of default classes to preserve, and merges the
class part of the object with the default. This way all the other properties are unaffected. Since our
wrapper_options is nil by default, it converts it to an empty object so it doesn’t error on use.
3c) Add the Method to the Template
With that, we now have an optional way to add classes and any other needed properties to our component. This makes it easier to use in a wider range of circumstances, and avoiding the need to make multiple versions with multiple helpers.