patbenatar / rbexy

A Ruby template language and component framework inspired by JSX and React
MIT License
33 stars 5 forks source link

Local-scoped components #110

Open guisehn opened 2 weeks ago

guisehn commented 2 weeks ago

Hello! First of all, thanks so much for building this project. I've always wanted to have this kind of template in Ruby/Rails, and it's amazing to see this library implementing it.

I'm currently evaluating using it in a personal project, but one feature that I couldn't find in Rbexy and that I think is very interesting is the ability to define local-scoped components, just like React allows you to define multiple function components in a file, and like Phoenix LiveView also allows doing with functional components.

Sometimes, a component, or a view, is made of smaller subcomponents that are not supposed to be global throughout the application. Global components promote reusability, but lots of times a component is not meant to be reusable. The issues I think we have with everything being global by default are:

Allowing multiple components to be defined in a single file and making them locally scoped would help address those points. I think this would require allowing the template to be defined inside the ruby file itself, which also has some challenges around code highlighting.

What do you think of the idea? Is this something that was already considered in the development of Rbexy?

patbenatar commented 2 weeks ago

@guisehn Hey! This is a great idea. I think this is more-or-less already doable in a couple of different ways, but perhaps could have some syntactic sugar added to make it a little nicer to do so:

Option 1: namespaced component classes

class RootComponent < Rbexy::Component
  class SubComponent < Rbexy::Component
  end
end

Templates would be root_component.rbx and root_component/sub_component.rbx

Technically SubComponent is still globally available as <RootComponent.Sub /> but I think the naming being within the RootComponent namespace makes the intention clear.

Option 2: manually render sub-templates

class RootComponent < Rbexy::Component
  def sub
    Rbexy.evaluate("<div>Some sub-template here</div>", self)
  end
end

And in root_component.rbx template:

<div>
  <h1>Root component</h1>
  {sub}
</div>

You could use a heredoc for longer templates:

class RootComponent < Rbexy::Component
  def sub
    Rbexy.evaluate(<<-RBX, self)
      <div>
        <p>Some more complicated template here</p>
      </div>
    RBX
  end
end

I haven't tested any of this but I think it's all possible. Curious to hear your thoughts.