New Hotwire Scaffolds | Index Tables?
div
and not a tr
but the old styling seems to be gone.I am also at a loss following the logic for the same model partial being rendered in the index collection and the show page. I get the idea to reduce redundancy but the show and index layouts would tend to never be the same.
For now I just replaced the
For the table change though I am happy with it - I usually have to remove the table structure anyway, so this change actually saved me some time. I guess adding a
render @model
part in the show page with the content in the _model partial, then work from there. This is a simple task but I think yeah usually the show and the index partial are not the same.For the table change though I am happy with it - I usually have to remove the table structure anyway, so this change actually saved me some time. I guess adding a
flex
and some space-x
to the partial class so it looks more like a tabled content, can address this issue pretty easily?Just to give something to the other side of the tables vs divs debate for scaffolding index pages.
Initially I felt the same, when I saw the tables were gone. I was sceptical and wanted to add a table back into my index.
However, the reality is all my Rails Apps needs to be mobile responsive and tables would never work long term.
It's been much easier and quicker to build mobile friendly UI's with the new system and tailwind.
I wonder if we could pass a flag with the scaffold generator to build an alternative index.html.erb with table when needed.
Or maybe just build 2 index pages: index.html.erb and index_table.html.erb
Then people can just rename the files if they want to use them (while also losing Hotwire in the index view).
Not sure my comments help. This idea might not be elegant enough!
Initially I felt the same, when I saw the tables were gone. I was sceptical and wanted to add a table back into my index.
However, the reality is all my Rails Apps needs to be mobile responsive and tables would never work long term.
It's been much easier and quicker to build mobile friendly UI's with the new system and tailwind.
I wonder if we could pass a flag with the scaffold generator to build an alternative index.html.erb with table when needed.
Or maybe just build 2 index pages: index.html.erb and index_table.html.erb
Then people can just rename the files if they want to use them (while also losing Hotwire in the index view).
Not sure my comments help. This idea might not be elegant enough!
tables vs divs is one thing. You can implement tables with divs if you need to.
The current index view is just a list of all of the collection in full glory using the same partial as #show.
I don't understand this as a default, the vast majority of apps want table-y looking things (whether implemented with div or table) not a giant page full of detail.
The current index view is just a list of all of the collection in full glory using the same partial as #show.
I don't understand this as a default, the vast majority of apps want table-y looking things (whether implemented with div or table) not a giant page full of detail.
The current index view is just a list of all of the collection in full glory using the same partial as #show.
I don't understand this as a default, the vast majority of apps want table-y looking things (whether implemented with div or table) not a giant page full of detail.
flex
'd out to a 'row'.Now granted, I agree with you. The generator spits out a #show that just renders the base partial for the record and I'd probably prefer the #show be separate from the base partial (probably just dupe the current base partial code) and the base-partial be
flex
'd... but it's not altogether a huge difference. Code-wise, at least. ok - I had a go at this, and I have a pretty good solution.
I'm showing meditations here, the goal is to have a single _meditation file which can be broadcast, and which can provide a full 'show' view, and a table-style 'index' view
CSS can do a _lot_ here! Specifically, it can make regular divs display like table rows/cells
Result first:
I'm showing meditations here, the goal is to have a single _meditation file which can be broadcast, and which can provide a full 'show' view, and a table-style 'index' view
CSS can do a _lot_ here! Specifically, it can make regular divs display like table rows/cells
Result first:
both of these update correctly with turbo
the key is in the css:
#/app/assets/stylesheets/custom/index.css
.index { display: table; width: 100%; border-collapse: separate; border-spacing: 0 1em; .index-row { display: table-row; } .index-cell { display: table-cell; vertical-align: middle; } .index-hide { display: none; } } .show { .show-hide { display: none; } }
so the meditation can be styled to show/hide appropriately:
#/app/views/meditations/_meditation.html.haml
%div.index-row{:id => "#{dom_id meditation}"} .show-hide = background_image_tag url_for(meditation.image.representation(resize_to_fill: [100, 100])), class: "index-cell", size: "100x100" .mb-4.index-hide = background_image_tag url_for(meditation.image.representation(resize_to_fill: [500, 500])), size: "500x500" .mb-4.index-cell %p.text-sm.font-medium.text-gray-500.index-hide Title = meditation.title .mb-4.index-cell %p.text-sm.font-medium.text-gray-500.index-hide Author = meditation.author .mb-4.index-hide %p.text-sm.font-medium.text-gray-500 Description = meditation.description .mb-4.index-cell %p.text-sm.font-medium.text-gray-500.index-hide Duration = meditation.duration minutes .mb-4.index-cell %p.text-sm.font-medium.text-gray-500.index-hide Published = meditation.published .mb-4.index-hide %p.text-sm.font-medium.text-gray-500 Key = meditation.slug .mb-4.index-cell.show-hide = link_to "View", meditation, class: "btn btn-white"
note - I'm showing a large image in the show page, and I don't want that to be loaded in the index page, so I'm using a helper for background_image_tag and making sure that the parent is hidden (taken from here: https://timkadlec.com/2012/04/media-query-asset-downloading-results/ )
#/app/helpers/meditations_helper.rb def background_image_tag(source,options = {}) options = options.symbolize_keys check_for_image_tag_errors(options) skip_pipeline = options.delete(:skip_pipeline) options[:style] = "background-image:url(#{resolve_image_source(source, skip_pipeline).html_safe});" #1px transparrent png https://stackoverflow.com/questions/5775469/whats-the-valid-way-to-include-an-image-with-no-src options[:src] = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgYAAAAAMAASsJTYQAAAAASUVORK5CYII=" options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size] tag("img", options) end
the helper is pretty much just tweaked from Rails' image_tag, so I may have missed some subtleties, but it works for me
then in the scaffold, I just have to make sure that the index page has the index class
then in the scaffold, I just have to make sure that the index page has the index class
/app/views/meditations/index.html.haml
... = tag.div id: ("meditations" if first_page?), class: "bg-white rounded-md shadow p-6 space-y-8 index" do = render partial: "meditations/index", object:
/app/views/meditations/_index.html.haml
-if meditations.count > 0 .table-header-group .table-row .table-cell.text-sm.font-medium.text-gray-500 Image .table-cell.text-sm.font-medium.text-gray-500 Title .table-cell.text-sm.font-medium.text-gray-500 Author .table-cell.text-sm.font-medium.text-gray-500 Duration .table-cell.text-sm.font-medium.text-gray-500 Published .table-cell.text-sm.font-medium.text-gray-500 = render partial: "meditations/index_row", collection: meditations, as: :meditation, cached: true
and
/app/views/meditations/_index_row.html.haml
/app/views/meditations/_index_row.html.haml
= turbo_stream_from meditation = render meditation
the show template is unchanged except for adding a 'show' class
... .p-8.bg-white.rounded.shadow.show = render partial: "meditations/meditation", object: @meditation, as: :meditation
and finally, tweak the broadcasts:
#/app/models/meditation.rb
#/app/models/meditation.rb
# Broadcast changes in realtime with Hotwire after_create_commit -> { broadcast_prepend_later_to :meditations, partial: "meditations/index_row", locals: {meditation: self} } after_update_commit -> { broadcast_replace_later_to self } after_destroy_commit -> { broadcast_remove_to :meditations, target: self }
this all works pretty well. Couple of imperfections:
1) if you have an empty table, then the broadcast of the first element won't trigger the header to show (you have to reload)
2) if you delete, then you leave an orphaned turbo_stream_from to the (now deleted) item
neither of those are a big deal for me.
I did a bit more thinking, and decided I didn't like the approach above.
Making the same template work for row & show felt too forced.
So - new approach. This time, two completely separate templates for the row and show views. They're pretty standard in _meditation_row and _meditation_show
the main change was to use a turbo-stream template for my update broadcast
from meditation.rb
Making the same template work for row & show felt too forced.
So - new approach. This time, two completely separate templates for the row and show views. They're pretty standard in _meditation_row and _meditation_show
the main change was to use a turbo-stream template for my update broadcast
from meditation.rb
# Broadcast changes in realtime with Hotwire after_create_commit -> { broadcast_prepend_later_to :meditations, partial: "meditations/meditation_row", locals: {meditation: self} } after_update_commit -> { broadcast_render_later_to self, locals: {meditation: self} } after_destroy_commit -> { broadcast_remove_to :meditations, target: dom_id(self, :row) }
where app/views/meditations/_meditation.turbo_stream.haml
= turbo_stream.replace dom_id(meditation, :row) do = render partial: "meditations/meditation_row", object: meditation, as: :meditation = turbo_stream.replace dom_id(meditation, :show) do = render partial: "meditations/meditation_show", object: meditation, as: :meditation
index has
... = tag.div id: ("meditations" if first_page?), class: "bg-white rounded-md shadow p-6 space-y-8 table w-full" do -if @meditations.count > 0 .table-header-group .table-row .table-cell.text-sm.font-medium.text-gray-500 Image .table-cell.text-sm.font-medium.text-gray-500 Title .table-cell.text-sm.font-medium.text-gray-500 Author .table-cell.text-sm.font-medium.text-gray-500 Duration .table-cell.text-sm.font-medium.text-gray-500 Published .table-cell.text-sm.font-medium.text-gray-500 = render partial: "meditations/meditation_row", collection: @meditations, as: :meditation, cached: true ...
(again - the table header won't get built if you broadcast the first item as an append)
and /app/views/meditations/_meditation_row.html.haml
and /app/views/meditations/_meditation_row.html.haml
%div{:id => dom_id(meditation, :row), class: "table-row-group mb-4"} = turbo_stream_from meditation %div.table-row{:id => dom_id(meditation, :row)} = image_tag url_for(meditation.image.representation(resize_to_fill: [100, 100])), class: "table-cell", size: "100x100" .table-cell.align-middle = meditation.title .table-cell.align-middle = meditation.author .table-cell.align-middle = meditation.duration minutes .table-cell.align-middle = meditation.published .table-cell.align-middle = link_to "View", meditation, class: "btn btn-white"
for show:
... .p-8.bg-white.rounded.shadow = render partial: "meditations/meditation_show", object: @meditation, as: :meditation
with /app/views/meditations/_meditation_show.html.haml
%div{:id => dom_id(meditation, :show)} = image_tag url_for(meditation.image.representation(resize_to_fill: [500, 500])), size: "500x500", class: "mb-4" .mb-4 %p.text-sm.font-medium.text-gray-500.index-hide Title = meditation.title .mb-4 %p.text-sm.font-medium.text-gray-500.index-hide Author = meditation.author .mb-4 %p.text-sm.font-medium.text-gray-500 Description = meditation.description .mb-4 %p.text-sm.font-medium.text-gray-500.index-hide Duration = meditation.duration minutes .mb-4 %p.text-sm.font-medium.text-gray-500.index-hide Published = meditation.published .mb-4 %p.text-sm.font-medium.text-gray-500 Key = meditation.slug
I'm not using
This means that the turbo_stream for the row is replaced every time. I don't know if that is a problem, but it seems to work ok...
Notifications
You’re not receiving notifications from this thread.