Open boardfish opened 2 years ago
@boardfish interesting. For the sake of comparison, can you provide example code for how you would accomplish this using partials?
You could do something like this:
<%# app/views/profiles/_profile.html.erb %>
<%= render (profile.complete? ? 'full_profile' : 'null_profile'), profile: profile %>
<%# app/views/profiles/index.html.erb %>
<%= render @profiles %>
I guess it's also achievable this way:
<%# app/views/profiles/index.html.erb %>
<% @profiles.each do |profile| %>
<%= render (profile.complete? ? 'full_profile' : 'null_profile')
<% end %>
I feel like components that entirely delegate off to other components like this based on their input could be quite an intuitive pattern, and may address a lot of the calls for a component being able to render multiple different templates.
render @profiles
That's the case I'd be most interested in supporting 👍🏻
Yeah, I think it really clearly marks the benefit of this as a feature. 👍
render @profiles
currently makes use of to_partial_path
as I understand it, so the implementation is directly linked to partials. I suppose what would be nice is for collection rendering to be able to use render_in
somehow... A little bit vague right now, but I might do a bit of digging soon to try and understand how best this could fit into ViewComponent and/or Rails.
Maybe we could mirror #to_partial_path
with a convention to define a #to_component
method on each item. This method would specify which component to use when rendering the item as part of a collection.
That's a great idea! So am I right in saying you're suggesting, for example:
class Person
def to_component
profile_complete? ? FullProfileComponent.new(self) : NullProfileComponent.new(self)
end
end
And it follows that:
<%= render @people %>
would call to_component
on each Person
to get the instances, right?
I wonder if there's a way to work slots into this, but I like where this is going.
Yes, that could be the idea. Thanks for taking the time to write the code samples :)
Here is a real world example:
# app/models/sections/slate.rb
class Sections::Slate < Sections::Base
# ...
def to_component
Sections::SlateComponent.new(
title:,
text:,
image: image_url,
button_text:,
button_link: button_linkable,
template:,
)
end
def to_admin_component
Admin::Sections::SlateComponent.new(
section: self,
)
end
Here's the front-office view:
<%# app/views/pages/home.html.erb %>
<% @page.sections.each do |section| %>
<%= render section.to_component %>
<% end %>
And I'm using render section.to_admin_component
in the back-office to render the component containing the fieldset to edit the section.
This example does not use slots but could serve as a starting point for thinking about how slots could work, for instance button_text
and button_link
could be good candidates for refactoring with a button
slot.
Feature request
I've just finished talking with @coder2000 about this. They were trying to render a collection, rendering different components based on another argument. So their aim was for
User::Component
to render a collection ofUser::Active
s, for example. It's got me thinking about how collections would work with dynamically chosen components — that seemed to be their original aim as they've mentioned in a discussion. There's been some talk about rendering the same component with different templates in the past, which I've done some work to try and establish patterns for, but this was a nice reminder that it still sort of needs to be thought about for collections.Let's say you're rendering a list of user profiles, some of which you'd like to show in less detail because they're incomplete, not visible, or otherwise. One way you could do that is to render
FullProfileComponent
s, and renderNullProfileComponent
s for those that aren't present. I think it would follow that you should be able to create a 'factory component' like this:The only thing stopping this at present is
validate_collection_parameter
, which should check.new
's parameters as well as#initialize
. I wanted to open up discussion before making any changes - what do folks think about this as a pattern?