← Back to Upcase

Design Patterns - deeply nested partials & conditional logic in views


(Ash Fogel) #1

Hey there learners,

I have a question regarding the structuring of complex views. I am working on an app and I feel like there are some anti-patterns creeping in, but I am unsure what to do about them.

The app has two kinds of users that interact in a marketplace - publishers and purchasers. The app is built entirely in a Restful style, and there are a number of resources and actions that both kinds of users interact with. This leads to the following kind of branching:

<% if current_user.publisher? %>
     # do something 
<% elsif current_user.purchaser? %>
     # do something else
<% end %>

Two good examples are in the dashboard#show, and listing#show views. Both kinds of users interact with these resources however they obviously necessitate a completely different representation of the data. Following on from the above, in the dashboard for one kind of user I might have the following:

 # iterate over one collection
 # iterate over a second collection
 # iterate over a third collection 
    --> for this third collection, create a partial for each item & populate a modal that allows the user to take some action for the item.
      --> for each modal, create a form accepting nested attributes for a fourth resource related to this third resource. 

So the result in my view code is the following:

  • Many conditionals
  • Deeply nested partials

Some of my views may have 20 partials.

Some of this branching is even starting to creep into my controllers. For example, in the Dashboard#show resource, I have code such as

<% if current_user.publisher? %>
    # populate these instance variables
<% elsif current_user.purchaser? %>
    # populate these instance variables
<% end %>

I realise I could hide this complexity in the controller by moving to a Presenter pattern, but it sort of seems to just pushing the problem around to another corner of the app.

So my question is - what are my options here? I’m obviously not the first person to have an app with lots of complex, conditional logic in my views, so I’m wondering if there are some recognised solutions. With respect to the examples above, my thinking is this:

  • The Dashboard for each user type is so fundamentally different that it could be split into its own resource. Eg PublisherDashboard, PurchaserDashboard. Two views, two controllers. There is no Dashboard model, the controller in this case is just pulling in collections of data from other models, so this seems like a reasonable approach.

  • However this is not the case in every situation. Eg listing#show is always going to be something where both kinds of users access the same kind of data, however the presentation of the data is wildly different. Conceptually I could split this two into two controllers, but then I’d end up with two views, two controllers, one backing model. This doesn’t seem like a good road to go down.

  • A client-side js framework seems to be where my app is heading, and I feel like it would help to have a clean interface between the presentation layer and the backing app. I’m learning ember.js for this reason.

But I’m guessing there must be an accepted Rails way of handling this kind of situation, or a design pattern I’m missing. If I continue down the path I’m on, my app will soon have 40-50 partials for one view, and this seems crazy. I’ll be getting into sub-folders of sub-folders haha.

I’m certain I can’t be the only person with this problem, so many apps have complex views with lots of conditional logic going on, so I’m really curious to know how they are put together.

Thanks so much in advance for any input, and if I can clarify anything please let me know!

Ash


(Ash Fogel) #2

As a note, I apologise for the < characters being escaped, I have no idea why this is happening… I cannot find the embed code block any longer, and was using the ‘enter pre-formatted text’ button to align the code blocks.


(John Dowd) #3

I’ve done 2 things in the past to deal with some of the complexity you’re dealing with (though I’ve never had 20 partials per view).

In an app where users have roles (users table is polymorphic with a role_type/id). I’d create a class for each role: Purchaser, Publisher, etc. In User.rb:

def events
  if user.role
    role.events #=> gets me to purchaser.events or publisher.events
  else
    # some sensible default if no role
  end
end

Now I can just use @events = current_user.events in the controller, without worrying about roles.

As for the nested iterations, perhaps you could use some kind of query object to get a hash to pass to the view? For example, to display the GPA of many students I would use the active record query interface to get a hash of id => gpa, rather than cycling through them individually. E.g.:

def student_gpas
  Score.group('student_id').average('grade')
end

You’d probably want to replace the keys with names rather than id’s, but you get the idea.

I hope either/both of those are helpful.


(Ash Fogel) #4

Hi there jdowd, just wanted to drop you a note and thank you for this reply - I especially like the first idea, I’m going to give that a whirl in the next couple of days.

thanks again,