The other day I was working on a new Rails application. Though I’m currently obsessed with Tailwind CSS, this project was a better fit for Bootstrap. I needed some predefined styling, quick. Unfortunately, fields with errors got in my way.
The app, like most web applications, required forms. Using Ruby on Rails’ (ActiveRecord) model validations, I was able to setup displaying error messages efficiently. I reached for a Bootstrap alert, shoved in a loop of error messages, and done.
Or, so I thought. 🤔
Isn’t it a better experience for the user if the fields that failed validation change border color? In fact, Bootstrap 4 has support for this.
The Problem(s) was Fields with Errors
Great! Except there’s one problem.
If you’re familiar with Rails, you’ll know that when the page rerenders after a failed form validation, the field failing validation is wrapped inside
In my case, I wanted to add the class name is-invalid to the failed input. In fact, “field with errors” doesn’t do much for me at all, since I’m not writing custom styling.
Writing custom styling would allow me to write some CSS, like this:
This would have been extremely powerful, and a boost in development speed had I been writing my own CSS.
In this case, though, we want to defer styling decisions to Bootstrap. As mentioned before, Bootstrap already has support for this. By writing this CSS above we’re hurting ourselves in one of two ways:
- We’re distancing ourselves from the styles provided by the CSS framework we’ve chosen to use. Or;
- We’re creating more work for ourselves by trying to match the Bootstrap style with custom CSS.
If you’ve encountered this problem before, you might know that you can override this functionality. The solution I like best is a Stack Overflow answer. Let’s start with the code in this solution, break down what it does, and then tweak it for Bootstrap 4.
1. Create an Initializer
Create an empty Ruby file:
2. Use the Code
Inside your newly created file, add the code.
Let’s talk about what this chunk of code does.
If your ActiveRecord backed form fails validation, ActionView calls this proc for each field that failed validation. The proc passes an html_tag (the form-field) and the instance into the block.
If we didn’t overwrite it, it would wrap the field in a div that looks something like this:
However, we’re going to override that by modifying the field directly, versus wrapping it in a div.
👆🏻 This variable tells us if the field already has a class attribute, or if it doesn’t.
If it does, we’ll get the index where class=” is in the raw HTML string. If it does not have class=” anywhere, we will set the variable to nil.
👆🏻 If the variable is not nil, we can append the class name error to the beginning of the class name list. The extra space is essential. If “class” already exists as an attribute on the form field, we want to keep the existing class intact.
👆🏻 If the variable is nil, right before we close the html_tag, we need to insert the entire class attribute assignment.
3. Modify the Code for Bootstrap 4
There are only a few changes I’ll make here.
The most important change is converting the class name from error to is-invalid. With this done, be sure to restart your Rails app as initializers load on boot.
In front of the argument “instance,” I added an underscore. This underscore may look silly to you, but it is a standard convention for block variables passed in and never used.
Do What Feels “Right”
There were many ways I could have Made This Work™. However, they would have felt like a hack.
However, with a little patience and research, it turns out Rails had the tools at my disposal all along.