-
Notifications
You must be signed in to change notification settings - Fork 413
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow passing arguments to a slot block #1810
Comments
I tried to have a go at this, but of course I see that it is not as simple as I imagined. In the end it is about the slot being rendered, as its Going deeper into the I think the proposed API above is probably not possible, while doing something like |
Hey @RolandStuder, thanks for bringing this up 😄 Please correct me if I'm wrong, but can't this be done by passing <%= render(TableComponent.new(product: product)) do |table| %>
<% table.with_column(label: "Price") { number_to_currency(product.price) } %>
<% end %> |
@RolandStuder do you think this accurately solves your issue or do you still feel its necessary for an alternative implementation |
@camertron @reeganviljoen No, the point is that I am want to render one slot multiple times with different data. I am rendering a (table) column cell for every entry in The problem is not that I can't pass in the products to one slot rendering instance, the problem is that a slot can kinda only be used once. I can't do that:
The component
On the other hand I also cannot do this:
As with this, I can't render the column headings, as there is a I think the abstraction I want is just a bit outside of what ViewComponent is indeted to support. When I researched the topic I was surprised to find out there was basically no one suggesting such a column focused approach. So I think it just abstracts a bit more than would people usually abstract in a VC. I think it's fine to just close this issue. I have a working workaround. The fact that is is hard to communicate and that it doesn't work like a regular slot, is probably a "smell" to say, "you shouldn't do this with ViewComponent" 😅 I described my approach also here, not sure if I communicated it better there. |
Hey @RolandStuder thanks for that additional information. Reading through your blog post provided the context I was missing, specifically this line:
Ahhhhh ok now everything makes sense! IMO, the fundamental problem you're running into here isn't ViewComponent, or even ERB - it's HTML itself. HTML definitely thinks of tables from a row perspective, and requires you to construct them row by row. ViewComponent would have to somehow maintain "cursors" at multiple points in the output buffer to write a table by columns, which I don't think is possible given how the Rails view layer is architected. In fact, I don't know any web framework that has this capability. The approach you shared here and in the blog post, that uses plain 'ol Ruby objects (POROs) instead of components, seems like a great way to solve the problem. Slots are not designed to be rendered multiple times, so we are unlikely to accept a change that would allow them or their In thinking about the problem from this fresh perspective, I've come up with an alternative approach that uses ViewComponents and includes limited trickery. Here's how the API looks: <%# app/views/something/index.html.erb %>
<%= render(TableComponent.new) do |table| %>
<% table.with_column(title: "Col1") do |column| %>
<% column.with_item { "val1" } %>
<% column.with_item { "val2" } %>
<% column.with_item { "val3" } %>
<% end %>
<% table.with_column(title: "Col2") do |column| %>
<% column.with_item { "val1" } %>
<% column.with_item { "val2" } %>
<% column.with_item { "val3" } %>
<% end %>
<% end %> There are three components at play here,
|
Feature request
In the case of some complex components, I want to create a abstracted API. The example I have is a
TableComponent
When I use a component, I can use the component instance in a block like:
The current API does not allow you to iterate through the rows and render all column with the data of said rows by declaring a slot like this. So in order to achieve that I had to create a work around (that I will present later). I wondered if others would find it a worthwhile addition to allow controlling what is passed into the block, so one can do:
Motivation
I some instances allowing to control what is passed into the slot block when rendering it, can allow for some really nice api, when iterating through collections.
Workaround
You can make the above API work by writing you own methods like:
Then in the component template you can do:
So this works wonderfully (though it took a while to figure it out).
So I got a solution for me, but wondered whether we might add this functionality to the core library, so it could be used like this:
Proposal
Allow to declare slots like normal
In the component template file, allow to pass args, that will be passed to the block upon rendering:
So in this case you would pass the row data into the block instead of the component, you could still pass the component as needed.
So you can use it in the view like this:
It would not be hard to adjust the view component code to adjust this. Though I am not sure if this kind of abstraction is what view component is striving for, especially since in this example you see the Column is not really a view component, but only an object to hold the block and params to configure the column, so it is muddying the responsability of the ViewComponent a bit.
ps: I would be happy to make a PR to enable this, I am also fine if this doesn't fit the vision of ViewComponent and the proposal is rejected.
The text was updated successfully, but these errors were encountered: