# frozen_string_literal: true
module Pages
class Views < ApplicationPage
def template
render Layout.new(title: "Phlex Views") do
render Markdown.new(<<~MD)
# Views
Phlex Views are Ruby objects that represent your app's user interface — from pages and layouts and nav-bars, to headings and buttons and links.
You can create a view class by subclassing `Phlex::HTML` and defining a `template` instance method.
MD
render Example.new do |e|
e.tab "hello.rb", <<~RUBY
class Hello < Phlex::HTML
def template
h1 { "👋 Hello World!" }
end
end
RUBY
e.execute "Hello.new.call"
end
render Markdown.new(<<~MD)
The `template` method determines what your view will output when its rendered. The above example will output an `
` tag with the content `👋 Hello world!`. Click on the "Output" tab above to see for yourself.
## Accepting arguments
You can define an initializer for your views just like any other Ruby class. Let's make our `Hello` view take a `name` as a keyword argument, save it in an instance variable and render that variable in the template.
We'll render this view with the arguments `name: "Joel"` and see what it produces.
MD
render Example.new do |e|
e.tab "hello.rb", <<~RUBY
class Hello < Phlex::HTML
def initialize(name:)
@name = name
end
def template
h1 { "👋 Hello \#{@name}!" }
end
end
RUBY
e.execute "Hello.new(name: 'Joel').call"
end
render Markdown.new(<<~MD)
## Rendering views
Views can render other views in their templates using the `render` method. Let's try rendering a couple of instances of this `Hello` view from a new `Example` view and look at the output of the `Example` view.
MD
render Example.new do |e|
e.tab "example.rb", <<~RUBY
class Example < Phlex::HTML
def template
render Hello.new(name: "Joel")
render Hello.new(name: "Alexandre")
end
end
RUBY
e.tab "hello.rb", <<~RUBY
class Hello < Phlex::HTML
def initialize(name:)
@name = name
end
def template
h1 { "👋 Hello \#{@name}!" }
end
end
RUBY
e.execute "Example.new.call"
end
render Markdown.new(<<~MD)
## Passing content blocks
Views can also yield content blocks, which can be passed in when rendering. Let's make a `Card` component that yields content in an `` element with a `drop-shadow` class on it.
MD
render Example.new do |e|
e.tab "card.rb", <<~RUBY
class Card < Phlex::HTML
def template(&content)
article(class: "drop-shadow") do
yield_content(&content)
end
end
end
RUBY
e.tab "example.rb", <<~RUBY
class Example < Phlex::HTML
def template
render Card.new do
h1 { "👋 Hello!" }
end
end
end
RUBY
e.execute "Example.new.call"
end
render Markdown.new(<<~MD)
The template in the `Card` view accepts a block argument `&content` and uses the `yield_content` method to yield it in an `` element.
The `Example` view renders a `Card` and passes it a block with an `` element.
Looking at the output of the `Example` view, we can see the `` element was rendered inside the `` element from the `Card` view.
## Delegating content blocks
Since the block of content was the only thing we need in the `` element, we could have just passed the content block to the element instead.
```ruby
class Card < Phlex::HTML
def template(&content)
article(class: "drop-shadow", &content)
end
end
```
MD
render Markdown.new(<<~MD)
## Registering custom elements
You can register custom elements with the `register_element` macro. The custom element will only be available in the view where it is registered and subclasses of that view.
MD
render Example.new do |e|
e.tab "example.rb", <<~RUBY
class Example < Phlex::HTML
register_element :trix_editor
def template
trix_editor input: "content", autofocus: true
end
end
RUBY
e.execute "Example.new.call"
end
render Markdown.new(<<~MD)
## Callbacks
Prepend the `Phlex::HTML::Callbacks` module, and if you define `#before_rendering_template` and/or `#after_rendering_template` method in your view, they will be called immediately before and after your template is rendered.
MD
render Example.new do |e|
e.tab "example.rb", <<~RUBY
class Example < Phlex::HTML
prepend Phlex::HTML::Callbacks
def before_rendering_template
h1 { "Hello" }
end
def template
h2 { "World" }
end
def after_rendering_template
h3 { "Bye" }
end
end
RUBY
e.execute "Example.new.call"
end
end
end
end
end