Components in Phlex 2.0
Quiz time: Old enough to remember Haml?
To some limited degree Phlex is like Haml -- I know; it's not fair to neither but please bear with me! Haml made it fun and easy to write HTML soup and had it not been for the strict formating I'd probably still be doing it!
Enter Phlex and all of a sudden your old habits die hard - again
Consider this tiny clip:
div(class:"m-2") do
plain { "Enter Phlex and all of a sudden your "}
a(href: "https://www.youtube.com/watch?v=2SkYNSKB1aM") { "old habits..." }
end
If you squint your eyes and let them just sweep the code block for a quick look-see? It could fool me - if I dropped the 'end' and added a '%' on the p's and a's :)
(Phlex even has a page in the new beta 2.0 documentation assigned to the difference between Haml and Phlex so I guess it's not all smoke and mirrors)
Anyways with that introduction off my chest let's get this band on the road!
Airborne
First I added the gem to my new-ish Rails project (it's actually running off of the main branch on github - so quite new in fact, but you don't have to be leaning over the edge to join the [phlex.]fun):
bundle add phlex-rails --version=2.0.0.rc1
I willingly stayed in character as the bonus pater familias and did the
bundle exec rails generate phlex:install
So, now I was ready to start porting my components library to Phlex 2.0 – components that I use for index views and forms, mainly and in this post I'll focus on the index views.
Getting the hang of it
"You cannot walk until you master crawling" is a life experience of mine so let's crawl for a start!
The Phlex documentation starts of with building a component but I'd like to introduce you to a slightly different learning curve. We'll build that index action on a home controller – in essence an endpoint like https://app.mortimer.pro/
Three Rails files comes to mind: views/layouts/application.html.erb
, views/home/index.html.erb
and controllers/home_controller.rb
From the docs we know where this is all going to end so let's do the obvious thing and write what we'd like to have
# controllers/home_controller.rb
class HomeController < ApplicationController
def index
render Views::Home::Index.new
end
end
Reloading https://localhost:3000 immediately confirms what we feared the most – something is missing (and if you're getting that dreaded almost blank screen informing you that there is an issue with security – I have just the medicin for you)!
Try this in your CLI (you could bring one up using [Cmd+j] if you're using VS Code with default keyboard shortcuts on a Mac) bundle exec rails generate phlex:view home::index
Another reload in the browser and we're once again good – kind'a
Inheritance and composition
So far we've managed little besides exchanging html.erb with rb but that is about to change – scouts honour!
Once again let's write what we want to end up having
# controllers/home_controller.rb
class HomeController < ApplicationController
layout false
...8<..
Reloading tell us that the layout went away. We'll fix that
First back in the CLI do bundle exec rails g phlex:component layout
Then follow up with these changes
# views/home/index.rb
class Views::Home::Index < Views::Base
def page_title = "Home"
def layout = Layout
...8<..
# views/base.rb
class Views::Base < Components::Base
PageInfo = Data.define(:title)
def around_template
render layout.new(page_info) do
super
end
end
def page_info
PageInfo.new( title: page_title )
end
end
We are one change short - but before we add that one allow me to introduce you to one of my absolute favorite web services: phlexing.fun
Its service is so simple and yet so powerful: have html -> get phlex
So copy the contents of your views/layouts/application.html.erb
and paste it into the textarea with the placeholder "Paste ERB here" and shortly after you'll have a copy-ready snippet of Phlex to paste into your layout.rb
(be prepared to follow the 'prompting' when reloading - your HTML will probably contain a number of calls to Rails specifics - like csrf_meta_tags
but Phlex has answers for most)
# components/layout.rb
class Components::Layout < Components::Base
def initialize(page_info)
@page_info = page_info
end
def view_template
doctype
html do
head do
title { @page_info.title }
meta(name: "viewport",
content: "width=device-width,initial-scale=1")
meta(name: "apple-mobile-web-app-capable",
content: "yes")
meta(name: "mobile-web-app-capable",
content: "yes")
csrf_meta_tags
# csp_meta_tag
# Enable PWA manifest for installable apps
# (make sure to enable in config/routes.rb too!)
# = tag.link rel: "manifest",
# href: pwa_manifest_path(format: :json)
link(rel: "icon", href: "/icon.png", type: "image/png")
link(rel: "icon", href: "/icon.svg", type: "image/svg+xml")
link(rel: "apple-touch-icon", href: "/icon.png")
# Includes all stylesheet files in app/assets/stylesheets
stylesheet_link_tag "tailwind",
"inter-font",
"data-turbo-track": "reload"
stylesheet_link_tag :app,
"data-turbo-track": "reload"
javascript_importmap_tags
end
body do
main(class: "container mx-auto mt-28 px-5 flex flex-col") do
yield
end
end
end
end
end
Agreed, this did not bring us Total World Domination or Peace To All Mankind but we took a decent step towards DRY-ing up and componentizing our views – more on that in the next post, and while I hurry to post that you could give this one a quick skim; it's on DRY-ing up the controller!