Lucky Component slots #1430
Replies: 4 comments 5 replies
-
I love this idea! I think that while the Proc approach works (I've done a LuckyCast on that approach here), something like what ViewComponent does would be awesome. They're still very much in the "figuring it out" phase since this is hard to get right from a usability perspective, but they've basically got three options at the moment: |
Beta Was this translation helpful? Give feedback.
-
Very rough draft I found to work: class PostComponent < Lucky::BaseComponent
def render
slots = Slots.new
yield slots
div class: "Post" do
div class: "PostHeading" do
slots.heading.try(&.call)
end
div class: "PostBody" do
slots.body.try(&.call)
end
end
end
struct Slots
property heading : Proc(IO::Memory) | Proc(Nil) | Nil
property body : Proc(IO::Memory) | Proc(Nil) | Nil
def heading(&block)
@heading = block
end
def body(&block)
@body = block
end
end
end
class PostsPage < MainLayout
def content
mount PostComponent do |c|
c.heading do
h1 "Welcome!"
end
c.body do
para "This is the body"
end
end
end
end |
Beta Was this translation helpful? Give feedback.
-
Another way of achieving "slots" is to "need" components If a post component needs a heading and a body and can have several images you could do something like this (assuming #1427 is implemented) class PostComponent < Lucky::BaseComponent
needs heading : HeadingComponent
needs body : BodyComponent
needs images : Array(ImageComponent)
def render
div class: "PostHeading" do
mount heading
end
div class: "PostImages" do
images.each do |image|
div class: "PostImage" do
mount image
end
end
end
div class: "PostBody" do
mount body
end
end
end
mount PostComponent,
heading: HeadingComponent.new(heading: "My Post"),
body: BodyComponent.new(body: "Blah Blah Blah"),
images: [ImageComponent.new(path: "img.jpg")] This, of course, means that you wouldn't be able to do all-custom html for any of the slots. You could have a generic component that yields in those cases? 🤷 |
Beta Was this translation helpful? Give feedback.
-
I haven't used the rails view component deal before, so I'm not familiar with what benefits it has that our current component system doesn't. But so I understand the motivation here, is it just to avoid using the proc syntax all over? Another idea I was thinking was, what if it looked like: class Card < Lucky::BaseComponent
# Add the type-safety that requires
slot header : HeaderComponent
slot body : BodyComponent
def render
div class: "Card" do
div class: "CardHeader" do
# this could be an issue as how do we know you want <#HeaderComponent>
# and not <header></header>
header.call
end
div class: "CardBody" do
# same... how do we know you don't want <body></body> here?
body.call
end
end
end
end
class HeaderComponent < Lucky::BaseCompoent
def render
yield
end
end
class BodyComponent < Lucky::BaseCompoent
needs context : HTTP::Context
def render
para context.request.path
yield
end
end
mount Card do |card|
card.header do
h1 "Welcome"
end
# maybe if the BodyComponent needs context, you can pass it in this way?
card.body(context) do
para "Welcome to my site"
img src: "welcome.png"
end
end half baked idea, but maybe that will give some brain juice flowing? |
Beta Was this translation helpful? Give feedback.
-
In Rails world, there's a gem called ViewComponent that is a lot like Lucky::Component. One of the features it has that we don't is the concept of slots https://viewcomponent.org/#passing-content-to-viewcomponents
We can kind of implement it by passing Procs as attributes
But we also have the ability to yield from a component so there could be a way to yield some sort of object that would allow us to update the API to look like this:
I believe this gives a much more pleasant API to passing multiple chunks of content.
I have no idea how to make this yielded object, though.
Thoughts?
Beta Was this translation helpful? Give feedback.
All reactions