palkan_tula
palkan
Frontend Invasion
Leads to separation (now we have back-end and
front-end engineers)
Makes Rails to serve only as an API provider
Increases development costs*
17
*Opinions expressed are my own, but I'm fine if you borrow them
Slide 18
Slide 18 text
palkan_tula
palkan
18
Is it possible to develop modern web application
without stepping out of the Ruby and Rails
comfort zone 🤔
palkan_tula
palkan
Phoenix LiveView
HTML elements «connects» to an Erlang process via
WebSocket
Process reacts on user interaction and internal server
events, updates the state, re-renders the affected
template parts and sends to the client
Client uses morphdom to perform a fast DOM patching
48
Slide 49
Slide 49 text
palkan_tula
palkan
“A new way to craft modern,
reactive web interfaces with
Ruby on Rails.”
49
palkan_tula
palkan
CableReady
A library to broadcast DOM modification
commands from server to browsers
Uses Action Cable as a transport
Uses morphdom to update HTML
51
Slide 52
Slide 52 text
palkan_tula
palkan
Example
52
...
<%= button_to item_path(item),
method: :delete,
remote: true do %>
...
<% end %>
Slide 53
Slide 53 text
palkan_tula
palkan
Example
53
# items_controller.rb
def destroy
item.destroy!
stream = ListChannel.broadcasting_for(item.list)
cable_ready[stream].remove(selector: dom_id(item))
head :no_content
end
Slide 54
Slide 54 text
palkan_tula
palkan
# items_controller.rb
def destroy
item.destroy!
stream = ListChannel.broadcasting_for(item.list)
cable_ready[stream].remove(selector: dom_id(item))
head :no_content
end
Example
54
$(" ##{dom_id(item)}").remove()
palkan_tula
palkan
StimulusReflex
Reflexes react on user actions and render HTML
responses
CableReady is use to send HTML to clients and to
update DOM
56
palkan_tula
palkan
Example
60
class ListReflex < ApplicationReflex
def toggle_item_completion
item = find_item
item.toggle!(:completed)
html = render_partial("items/item", {item})
selector = dom_id(item)
cable_ready[
ListChannel.broadcasting_for(item.list)
].outer_html(selector:, html:)
cable_ready.broadcast
morph_flash :notice, "Item has been updated"
end
private def find_item
Item.find element.dataset["item-id"]
end
end
Slide 61
Slide 61 text
palkan_tula
palkan
Example
61
class ListReflex < ApplicationReflex
def toggle_item_completion
item = find_item
item.toggle!(:completed)
html = render_partial("items/item", {item})
selector = dom_id(item)
cable_ready[
ListChannel.broadcasting_for(item.list)
].outer_html(selector:, html:)
cable_ready.broadcast
morph_flash :notice, "Item has been updated"
end
private def find_item
Item.find element.dataset["item-id"]
end
end
Broadcast DOM to all connected
clients
Show flash-notification to the current
user
Object representing the current
element data attributes
Slide 62
Slide 62 text
palkan_tula
palkan
Example
62
class ApplicationReflex < StimulusReflex ::Reflex
private
def morph_flash(type, message)
morph "#flash", render_partial(
"shared/alerts",
{flash: {type => message}}
)
end
end
Slide 63
Slide 63 text
palkan_tula
palkan
StimulusReflex
Stable & Mature (v3.4)
Comprehensive documentation
Active Discord community (>1k members)
Works with AnyCable out-of-the-box 😉
63
Slide 64
Slide 64 text
palkan_tula
palkan
More HTML-over-WS
Motion
Turbo Streams
64
palkan_tula
palkan
78
# app/components/like_button.rb
class LikeButton < Button ::Component
def initialize
super(label: I18n.t("like"), icon: "❤")
end
end
# some.html.erb
<%= render LikeButton.new %>
Example
Slide 79
Slide 79 text
palkan_tula
palkan
79
# test/components/button_test.rb
class Button ::ComponentTest < ActiveSupport ::TestCase
include ViewComponent ::TestHelpers
def test_render
render_inline Button ::Component.new(label: "Test")
assert_selector "button.btn", text: "Test"
assert_no_selector "button.btn i"
end
def test_render_with_icon
render_inline Button ::Component.new(label: "Test", icon: "✔")
assert_selector "button.btn", text: "Test"
assert_selector "button.btn i", text: "✔"
end
end
Example
Slide 80
Slide 80 text
palkan_tula
palkan
View Component
Plays nicely with Rails (Rails way)
Faster rendering (up to 10x faster than partials)
Preview functionality (similarly to mailers)
80
Slide 81
Slide 81 text
palkan_tula
palkan
Preview Component
81
Slide 82
Slide 82 text
palkan_tula
palkan
View Component
82
app/
frontend/
components/
banner/
component.rb
component.html.slim
component.css
component.js
Keep HTML, CSS, JS, etc. together
palkan_tula
palkan
CSS after Bootstrap
Bulma
TailwindCSS
Shoelace
88
Slide 89
Slide 89 text
palkan_tula
palkan
Mobile-first
CSS-only
Modular and customizable (via Sass vars)
Could be enhanced by Vue components (Buefy)
89
Slide 90
Slide 90 text
palkan_tula
palkan
90
Bulma + Stimulus + Buefy
Slide 91
Slide 91 text
palkan_tula
palkan
Utility-first (no components, just classes)
Components extraction mechanism (@apply)
Fast prototyping (+playground)
Optimized production and development builds
(JIT)
91
Slide 92
Slide 92 text
palkan_tula
palkan
92
Slide 93
Slide 93 text
palkan_tula
palkan
Web Components
Customizable
Accessibility
93
Slide 94
Slide 94 text
palkan_tula
palkan
94
<=%=body%>
Slide 95
Slide 95 text
palkan_tula
palkan
What to choose?
Bulma—admin dashboards, or you know some Vue
Shoelace—CRUD, admin dashboards
TailwindCSS—user-facing interfaces
95
palkan_tula
palkan
Frontendless Rails Way
Could be used instead of JS/SPA approach for
applications with not-so-tricky UI (dashboards,
CRUD-s, etc.)
Increases productivity (though not for free)
We're just in the beginning of the New Era!
106