As our applications grow larger and larger, it starts to become obvious that some parts of them grow messier than others.
For Rails applications in particular, the framework provides some structure for your back-end code, but it doesn’t really offer any structure to your front-end code.
This is on purpose and it’s great to have this freedom — although sometimes freedom can be the final nail in the coffin.
In the spirit of Rails is omakase: Sometimes it’s better to let the chef decide, or in this case, just use a battle-tested framework.
But there’s a problem. We don’t always need a framework. If all we need to do in our application is tie together a few Select2 drop-downs, a slider, some modals and maybe a date picker or two, then surely a framework is overkill.
Sometimes jQuery is enough, and for most Rails applications this approach “just works”. The problem is that as the application grows over time, because there is no well-defined structure to the front-end code, it deteriorates more and more until it feels like it’s all tied up with paperclips, bubblegum and wishful thinking. Make one small change in one file and everything breaks, I’m sure we’ve all seen this.
I’ll use SASS for the CSS and HAML for the HTML. Again, this might just as easily be SCSS and HTML, or whatever you are already using.
A place for everything and everything in its place
It works like this. You add a few data-attributes on your application layout:
So when the HTML looks like this:
Then it will run
The implementation itself might vary. It normally takes less than 50 lines of code to tie everything up. You can find an implementation here.
We also want to avoid situations where changing one script causes others to break. This sort of problem can commonly happen as a result of a) poor isolation (an object knows too much about the external world), or b) violation of the Single Responsibility Principle, which is when an object does too many things.
Not only is the
show method doing too much in this example – managing the modal state on the screen and initializing other components – but if the views which use
my-element ever change the class of the element (or remove the element altogether), that’s gonna break. And if it fails silently, that’s not easy to see. You can spend quite some time hunting down this kind of bug. Eventually, maintaining the application feels like walking on eggshells.
With this isolation, and by upholding the Single Responsibility Principle, it is possible to change one file and not break others.
A component-oriented approach would look like this:
Notice that communication is still required between
Comments#Index, but we invert the responsibilities of the objects by using events.
Modal just lets consumers know that something related to the modal is going on. Consumers might listen to this event and do their work if they want.
Modal doesn’t need to know about this, and will never need to change if the markup changes.
Now if we change the
app/views/comments/index.html.haml view, we know that we might break
It might seem like a small thing, but having this 1-to-1 relation is great. We don’t have to hunt things down, and we know that the only parts which are dependent on the markup are the controllers and – to some extent – the components.
Communication between components
Most likely you will need your components to talk to each other. You can easily achieve this by using PubSub.
PubSub is just an object which exposes
trigger methods. The names may differ, but basically it allows you to bind callbacks to events, unbind them, and trigger events. There are many implementations, but my favorite is this tiny one. It basically delegates everything to jQuery, which you probably are using anyway. If you don’t use jQuery, you can search for alternatives or write your own PubSub implementation. It’s pretty easy.
If you do use jQuery, your components might look like this:
Feel free to create more folders and categories as you see fit.
CSS and HTML
This is where BEM comes to the rescue. It doesn’t have to be BEM, though. Some people dislike it because, let’s admit it, it’s ugly. But it’s modular, it’s organized, it’s easy, and it works. We really want all of those things in legacy applications.
BEM stands for “Block/Element/Modifier”. A block is a container: a box in the page with some functionality, like a button, a navigation bar, a modal. A block can have many elements.
An element only makes sense inside a block. For example, if you want to display a button inside a modal, we know that modal is a block. And while the button is inside the modal, the button is actually its own block.
So in this case, because
button makes sense outside of a modal, it gets to be its own component. On the other hand, the button to close the modal makes sense as an element, but it doesn’t really make sense without a modal.
Finally, modifier is simply state. For example, the modifiers of a button could be: active, disabled, primary, danger, hidden.
A BEM CSS rule starts with a selector that might look like this:
That selector tells you that the style declared in the rule is applied to the
active modifier of the
some-element element inside the
my-block block. It’s not pretty, but it works surprisingly well.
As for the HTML, Rails’ standard solution of partials + helpers is a nice approach. Our final component looks like this:
Isn’t that cool?
This approach, while not ideal for front-end heavy applications, is in my opinion a pretty maintainable solution which requires no libraries and just a few conventions.
While Backbone is not perfect, it’s interesting the way design patterns applied to different codebases end up with similar-looking code. Refactoring is the way to clean code.
Hope you had a nice read. See you next time! 🙂