A question about "the Rails way" of organizing views and partials

Recently I started to experiment with turbo-frames, specifically lazy loading frames. In this process and the way I approached it, I noticed something that made we wonder whether there is a “Rails-y way” of organizing partials and views that I’ve missed.

Here’s the setup:

  1. Model Foo has_many model Bars.
  2. Both Model Foo and Bar have corresponding controllers (e.g., foos_controller and bars_controller) with all the normal actions and views.
  3. Foo’s controller’s show action/view wants to show all of the Bars it has.

All good so far. Now the experiment: Alter the foos_controller show view to include a turbo-frame with a src to display the Bars.

Here’s how I approached this:

  • Create a sub-resource route on foos for the bars:
  resources :foos do
    resource :bars do
  end
  • Create a turbo-frame this looks like this:
<turbo-frame id="bars" src="/foos/1/bars" loading="lazy"></turbo-frame>

Here’s where it got interesting (for me…a relative Rails novice)…

The route /foos/1/bars routes to the bars_controller’s index action (passing with it foo_id = 1 as a parameter)!

Hmmmm. Okay.

To this point I had a _bars.html.erb partial in the views/foos folder.

This is where I started wondering…

If I use this approach, I need to create (or move) the views/foos/_bars.html.erb over to views/bars/index.html (or similar) and simply have the bars_controller’s index (or similar) action render (with only the Bars for the foo_id).

Granted, I don’t need to structure routes (and controllers) this way. But it just got me wondering…

So with all of that (long-winded) back story, my question is this:

Is it considered the “Rails way” (if there is such a thing) that all views and partials (even when rendered from another model’s controller) should “live with” (i.e., be co-located with) the controller for that model?

In other words, using this example…

Should all Bar-related views and partials live in views/bars and be referenced from any other view that needs them?

(And vice-versa for Foo-related views and partials.)

Is this the preferred way? Is this a “cleaner” way (also possibly DRY-er)? Is this what a much more experienced Rails developer would expect if they joined my project?

P.S. This isn’t about designing and structuring things around turbo-frames. That just happened to be what raised this question in my mind.

Thanks in advance for anyone who has advice or opinions on this!

1 Like

You should pluralise the bars route and skip the do if you’re not nesting anything further:

resources :foos do
  resources :bars
end

I would say no, it isn’t. While it’s true that the scaffold methods create this kind of folder structure, models, views and controllers are fully independent and frequently used as such. There’s nothing wrong with ignoring the bars route and accessing a foo's bars directly in the foo views. I usually have many more models than I have controllers, and even fewer directories in /app/views. From a purely semantic perspective I would think having a _bars.html.erb partial in /app/views/foos/ is perfectly reasonable. In some cases that might be the only (non admin) bars related view in an application.

Thanks. I appreciate the input!

I may be missing your point a bit…

Rails controllers are one level and don’t depend on each other actually. You need only BarsController to render /foos/1/bars and FoosController may even be missing from the system to do that.

That route calls BarsController#index by default and its default view to render is bars/index.*. It’s a full view to render a separate HTML response. I see no reason to use a partial for that and to store it in foos/_bars.*.

The only connection to /foos is a route and foo_id being passed to the action. Based on its presence, you can render foos/_bars.* inside of bars/index.*, but its not a straightforward Rails way for sure.

Interesting question. I would say that the most Rails-y way to do this would be to render the bars as a collection. Basically, you would omit the nested :bars resource and simply have a partial views/bars/_bar.html.erb with the view for one single bar.

Then in the foo show view you simply call:

<%= render @foo.bars %>

The rails guide section I linked above has details on additional options when rendering a collection. This approach is DRYer as you can re-use the _bar partial for other associations (e.g. Baz that has_one :bar, the Baz views can render @baz.bar).

To your other questions, you should disentangle the concepts of controllers, routes, and views. A partial in views/bars doesn’t “live with” the bars controller, and you can render that partial from any other view anywhere else in your app. It’s probably where I’d look for the partial first, but it’s not the only way to organize it. If you have a special way of displaying bars in the context of the foo that they belong_to, then it could make sense to have a partial for that in views/foos.

Basically, as others pointed out there are many ways to do this, and the solution that makes sense will depend a lot on the context and data structures in your app. If you use the above approach don’t forget to preload the bars in your controller!

Thanks for all the feedback. It has helped me to think about the best ways of approaching this.