Components with Phlex in Rails

February 5, 2024

Track your progress

Sign in to track your progress and access subscription-only lessons.

Log In

Your Teacher

Hey, I'm Adam! I spend my days coding and writing for Judoscale—the autoscaling service I created in 2016. I'm @adamlogic pretty much everywhere online.

About This Episode

Adam McCrea walks through using Phlex to build components for views in your Rails applications. Phlex is a Ruby gem for building fast object-oriented HTML and SVG components using Ruby constructs: methods, keyword arguments and blocks.

Notes

Resources

class AdapterSetupComponent < ApplicationComponent
  def initialize(app, process_type)
    @app = app
    @process_type = process_type
  end

  def template
    render ModalComponent.new do |modal|
      modal.parts do
        modal.header "Adapter Installation",
          previous_href:
            url_for([:new, @app, @process_type, :adapter_setup])

        modal.content do
          div(class: "p-8 prose max-w-none") do
            render "adapter_setups/languages/#{@app.language.downcase}"
          end
        end

        modal.footer do
          form_with model: @app,
            url: [@app, @process_type, :adapter_setup],
            method: :get,
            builder: StyledFormBuilder do |form|
            plain helpers.styled_button "Finished and Deployed",
              background: :gray,
              icon: "circlecheckmark"
          end
        end
      end
    end
  end
end
class ApplicationComponent < Phlex::HTML
  include Phlex::Rails::Helpers::Routes
  include Phlex::Rails::Helpers::ContentFor
  include Phlex::Rails::Helpers::LinkTo
  include Phlex::Rails::Helpers::FormWith

  if Rails.env.development?
    def before_template
      comment { "Before #{self.class.name}" }
      super
    end
  end
end
class ModalComponent < ApplicationComponent
  def template(dismissable: true, &block)
    content_for(:modal_content) do
      render "modals/modal_wrapper", dismissable: dismissable, &block
    end
  end

  def parts(hug: false, &block)
    # This wrappers assumes three child nodes (header, content, footer).
    div class: "grid grid-rows-[auto_1fr_auto] #{"min-h-[36rem]" unless hug}", &block
  end

  def header(title, dismissable: true, previous_href: nil)
    div(class: %(relative px-4 bg-gray-100 h-16 flex justify-center items-center)) {
      if previous_href
        link_to previous_href, class: "absolute left-4 text-gray-400 h-8 w-8" do
          plain helpers.svg(:chevronleft)
          div(class: %(sr-only)) {
            %(Back)
          }
        end
      end

      if dismissable
        button(type: %(button), class: %(absolute right-4 text-gray-400 h-8 w-8), "click.prevent": %(modalVisible = false)) {
          plain helpers.svg(:close)
        }
      end

      h2(class: %(text-gray-600 font-medium)) {
        title
      }
    }
  end

  def content(&block)
    div class: "max-h-[70vh] overflow-auto", &block
  end

  def footer(&block)
    div class: "relative p-4 bg-gray-100 flex justify-end items-center gap-4", &block
  end
end

Want to stay up-to-date with Ruby on Rails?

Join 82,464+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.