This being the last article in the series, hopefully by now you have an idea of the concept. If this is the first time you’re reading anything from this series, here’s is the gist:
Any software developer who deals with MVC frameworks has most likely come across the mantra “Fat Model, Skinny Controller”. This is not a good philosophy. It is a bad idea to have a fat model. In fact, it is a bad idea to have a fat anything. Instead, you should strive for Skinny Everything.
With Rails, there are a lot of ways to skinnify your files. Part 1 discussed the advantages of using Decorators to keep display logic out of your models. Part 2 talked about concerns, which are used to extract responsibility-specific code out of models and controllers. Concerns can not only lead to skinnier models (and controllers), but also allow for reuse of that functionality in other models by simply including the concern at the top of the file. Both of these methods are great for keeping a skinny model. This final part in the “Skinny Everything” series is going to cover the simplest way to keep your Views skinny.
Partials
Partials are what the name suggests: partial pieces of view templates. They are used to break complex views into smaller, more manageable pieces. Similarly to concerns, partials can be reused in multiple places. This means that using partials not only leads to skinnier view files, but also to more DRY code.
Creating a partial is super easy, all you have to do is precede the file name with an underscore. Then to render it within a view, all you need to do is:
<%= render 'partial_name' %>
(note that you don’t precede with an underscore when you render it)
Rails expects us to use partials all the time. The most obvious example of a partial expected by Rails is the form partial. Take a basic controller, lets say “ProfilesController” (app/controllers/profiles_controller.rb). Rails assumes that this controller has a corresponding views directory which contains the profile view templates (app/views/profiles). This folder should contain templates for any CRUD actions in the controller that need a view. Lets say we need show.html.erb, new.html.erb, and edit.html.erb. In most cases, the new and the edit views are going to need the same form. So, why write the same form twice? The common practice is to add another file: apps/views/profiles/_form.html.erb which you can then render in both the edit and new templates with a simple
<%= render 'form' %>
Already familiar with using form partials like this? Awesome. Well there are a few more cool things you can do in addition to this basic implementation.
From other directories
Lets say you want to render your signup form on the home page. You already have the sign up form in its own partial (Go You!) which is located at app/views/registrations/_sign_up_form.html.erb. Now why doesn’t this work?
<%= render 'sign_up_form' %>
Well, you need to be more specific with the partial’s path, since the home page is located in a foreign directory (app/views/home vs. app/views/registrations).
<%= render 'registrations/sign_up_form' %>
With an instance variable
Sometimes you have a bit of code that you want to move to partial, but it has a couple places that call on an instance variable which you defined in the controller action. This instance variable isn’t gonna be defined in the partial by default. In this case, just pass an object parameter with that instance variable when you render:
<%= render partial: 'products/product', object: @product %>
If you want to send an object to a partial like this, you need to specify “render partial:”, not just “render”. You will now be able to access @product within the partial.
With multiple instance variables
Similar situation to the above, except you need to pass multiple objects into the partial. Same idea, except you pass a hash of objects to the “locals” parameter instead of one object to the “object” parameter:
<%= render partial: 'products/product', locals: { profile: @profile, product: @product } %>
One important difference between this and the object method above is that within the partial, the variables are referenced by the keys in the locals hash. Meaning you will need to call profile
and product
in the partial as opposed to @profile
or @product
. Also note that you also have to specify render partial
for this to work.
While it is pretty sweet that you can pass as many instance variables as you want to a partial like this, you really
should define as few instance variables as possible for each controller action. At Revelry, we try to define only one instance variable per controller action.
Rendering Collections
Probably the niftiest feature of partials is that you can render a collection with just one line. Sure, you could just do this everywhere:
<% @products.each do |product| %>
<%= render partial: 'products/product', locals: { products: product } %>
<% end %>
But thats more than you need. Assuming that you have a partial named _product.html.erb, this will work:
<%= render @products %>
Check out this article by Joël Quenneville for more on rendering collections.
Conclusion
Are your view files are starting to resemble a mountain range? Are they getting to be marginally obese? Time to partialize some templates, amirite?
We're building an AI-powered Product Operations Cloud, leveraging AI in almost every aspect of the software delivery lifecycle. Want to test drive it with us? Join the ProdOps party at ProdOps.ai.