Module: Phlex::Reactive::Component
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/phlex/reactive/component.rb
Overview
Component turns a self-contained Phlex component into a Livewire-style reactive unit: declare actions in Ruby, and the generic ‘reactive` Stimulus controller wires clicks/inputs to an HTTP round trip that re-renders the component and morphs it back into the DOM. No per-feature Stimulus controllers, no hand-picked Turbo targets.
Include alongside Phlex::Reactive::Streamable (which provides #id and the re-render machinery).
Security model (the decisive design choice) ===
We do NOT ship component STATE to the browser (no snapshot). The DOM carries a signed IDENTITY:
* Record-backed (the common case): reactive_record :todo signs the
record's GlobalID. The server re-finds it via GlobalID — the client
can neither forge the component class nor swap the record. State =
the database.
* State-backed (record-less, e.g. a counter): reactive_state :count
signs the listed instance variables. Use only when there is genuinely
no record to re-find.
Actions are DEFAULT-DENY: only methods declared with ‘action :name` may be invoked. The signature proves the token is ours, NOT that this user may act — your action must still authorize the record. Action params pass through a declared schema; nothing else reaches the method.
Usage (record-backed):
class Todos::Item < ApplicationComponent
include Phlex::Reactive::Streamable
include Phlex::Reactive::Component
reactive_record :todo
action :toggle
action :rename, params: { title: :string }
def initialize(todo:) = @todo = todo
def id = dom_id(@todo)
def toggle = ((@todo, :update?); @todo.toggle!(:done))
def rename(title:) = ((@todo, :update?); @todo.update!(title:))
def view_template
li(id:, **reactive_attrs) do
(**on(:toggle)) { @todo.done? ? "✓" : "○" }
span { @todo.title }
end
end
end
Defined Under Namespace
Classes: Action
Instance Method Summary collapse
-
#on(action_name, event: "click", **params) ⇒ Object
Attributes for an element that triggers an action.
-
#reactive_attrs ⇒ Object
Root-element attributes: marks the element reactive and carries the signed identity token.
Instance Method Details
#on(action_name, event: "click", **params) ⇒ Object
Attributes for an element that triggers an action.
button(**on(:toggle)) { "○" }
form(**on(:save, event: "submit")) { ... }
Extra keyword args become explicit params merged over collected form fields. For click triggers we force type=“button” so a bare button inside a <form> can’t submit it and cause a full-page navigation.
138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/phlex/reactive/component.rb', line 138 def on(action_name, event: "click", **params) attrs = { data: { action: "#{event}->reactive#dispatch", reactive_action_param: action_name.to_s, reactive_params_param: params.to_json } } attrs[:type] = "button" if event == "click" attrs end |
#reactive_attrs ⇒ Object
Root-element attributes: marks the element reactive and carries the signed identity token. Spread onto the root:
div(id:, **reactive_attrs) { ... }
122 123 124 125 126 127 128 129 |
# File 'lib/phlex/reactive/component.rb', line 122 def reactive_attrs { data: { controller: "reactive", reactive_token_value: reactive_token } } end |