Blocks, slots, & yielding

One of the most powerful features of Phlex is how well it can handle blocks of content. If you’re familiar with custom HTML elements or other component frameworks, you may have heard this referred to as “slots”.

Default block from a render

A very common way to use blocks is to render a component with a default block of content. This is done by calling the render method on the component class and passing a block of content to it. This block is called when the view_template method is invoked during the rendering process.

class Card::Component < Phlex::Component
  def view_template(&block)
    div(class: "card") do
      h1(class: "card-title") { "Hello, world!" }
      yield
    end
  end
end

render Card::Component.new do
  p { "This is a block of content." }
end

Multiple blocks

What about components that have multiple blocks? For example, the h1 tag in the card component should accept any block of content we pass it when rendering.

class Card::Component < Phlex::Component
  def view_template(&block)
    div(class: "card") do
      yield
    end
  end

  def title
    h1(class: "card-title") { yield }
  end
end

When we call it, we can pass a block of content to the title method.

render Card::Component.new do |card|
  card.title { "Custom title" }
  p { "This is a block of content." }
end

Now when we render the card, the title method is called from the Card::Component class, giving us the decorated h1 tag with the content we passed it. Then the p tag is rendered with the content we passed it.

Order matters

When we render multiple blocks, the order in which they are rendered is important. The first block passed to the component will be rendered first, followed by any additional blocks.

render Card::Component.new do |card|
  p { "This is a block of content." }
  # The title would be on the bottom, which we might not want!
  card.title { "Custom title" }
end

What do you do about content where you can’t control the order of the blocks?

Deferred rendering

Sometimes the order of items that need to be rendered isn’t known when writing a component. In other cases, you might want to enforce the order of when items are rendered, regardless of when they’re invoked from a Phlex view.

In that case, we’ll vanish the block from the view template and render it later.

class Card::Component < Phlex::Component
  def view_template(&)
    vanish(&)

    div(class: "card") do
      h1(class: "card-title", &@title)
      div(class: "card-content", &@content)
    end
  end

  def title(&block)
    @title = block
  end

  def content(&block)
    @content = block
  end
end

Here’s what it looks like when we render it.

render Card::Component.new do |card|
  card.title { "This is a block of content." }
  card.content do
    p { "Custom title" }
  end
end

We can call it in whatever order we want, and the order doesn’t matter since the blocks are rendered in the order they were passed to the component.

render Card::Component.new do |card|
  card.content do
    p { "Custom title" }
  end
  card.title { "This is a block of content." }
end

Checkout in minutes

Use Apple Pay, Amazon Pay, or your credit card to order this course and we'll email you the receipt.

Purchase video course for $379