ruby-on-railsrubyphlex-railsphlex

Phlex multiple yield


Is it possible for a Phlex component to yield more than once? For instance

class Component < Phlex::HTML
  def view_template
    div do
      yield
      
      span { plain "component" }

      yield
    end
  end
end

So the rough idea is to use like

<%= Component.new do %>
  <div>foo</div>

  <!-- and have the span with content "component" rendered here -->

  <div>bar</div>
<% end %>

Of course this won't work, but is there a way to make it work in the expected way? Preferably without having to pass div with content foo and div with content bar as an argument to view_template


Solution

  • You can yield something out, like a proc, you can use in the template:

    class Component < Phlex::HTML
      def view_template
        div do
          yield -> do
            span { plain "component" }
          end
        end
      end
    end
    
    <%= render Component.new do |span| %>
      <div>foo</div>
      <%= span.call %>
      <div>bar</div>
    <% end %>
    

    Or better define a separate method:

    class Component < Phlex::HTML
      def view_template
        div do
          yield # this defaults to yield(self)
        end
      end
    
      def content
        span { plain "component" }
      end
    end
    
    <%= render Component.new do |c| %>
      <div>foo</div>
      <%= c.content %>
      <div>bar</div>
    <% end %>
    

    Both render:

    <div>
      <div>foo</div>
      <span>component</span>
      <div>bar</div>
    </div>
    

    https://www.phlex.fun/components/yielding.html


    You could also double yield, but you'll probably want to implement some sort of a slot functionality instead. But here is a simple example:

    class Component < Phlex::HTML
      def view_template
        div do
          yield :top
          span { plain "component" }
          yield :bottom
        end
      end
    end
    
    <%= render Component.new do |section| %>
      <% if section == :top %>
        <div>foo</div>
      <% end %>
      <% if section == :bottom %>
        <div>bar</div>
      <% end %>
    <% end %>