Components provide real-time, stateful UI elements that update automatically via WebSocket.
class MyComponent
include Azu::Component
def content
"<div>Hello</div>"
end
endReturn the HTML content of the component.
def content : String
<<-HTML
<div id="my-component">
<p>Count: #{@count}</p>
</div>
HTML
endReturns: String - HTML content
Called when component is first connected.
def mount(socket)
@socket = socket
load_initial_data
push_state
endParameters:
socket- WebSocket connection
Called when component is disconnected.
def unmount
cleanup_subscriptions
save_state
endPush current state to client, triggering re-render.
on_event "increment" do
@count += 1
push_state # Sends updated HTML to client
endAppend content to an element.
def add_item(item)
@items << item
push_append("#items-list", render_item(item))
endParameters:
selector : String- CSS selectorhtml : String- HTML to append
Prepend content to an element.
def add_notification(notification)
push_prepend("#notifications", render_notification(notification))
endReplace an element's content.
def update_status(status)
push_replace("#status", "<span>#{status}</span>")
endRemove an element.
def remove_item(id)
@items.reject! { |i| i.id == id }
push_remove("#item-#{id}")
endDefine event handlers.
on_event "click_button" do
handle_button_click
end
on_event "submit_form" do |data|
name = data["name"].as_s
process_form(name)
endParameters:
event_name : String- Event name from client&block- Handler block (optionally receives event data)
<button azu-click="click_button">Click Me</button>
<button azu-click="delete" azu-value="123">Delete</button>
<input azu-change="input_changed" azu-model="name">
<form azu-submit="submit_form">...</form>Event Attributes:
azu-click- Click eventazu-change- Change eventazu-submit- Form submitazu-keyup- Key up eventazu-keydown- Key down eventazu-focus- Focus eventazu-blur- Blur event
Data Attributes:
azu-value- Value to send with eventazu-model- Two-way data binding
Define component properties.
class UserComponent
include Azu::Component
property user_id : Int64
property show_details : Bool = false
def mount(socket)
@user = User.find(user_id)
push_state
end
endUsage in HTML:
<div azu-component="UserComponent" azu-props='{"user_id": 123, "show_details": true}'></div>class CounterComponent
include Azu::Component
@count = 0
@history = [] of Int32
def content
<<-HTML
<div>
<p>Count: #{@count}</p>
<button azu-click="increment">+</button>
<button azu-click="decrement">-</button>
<button azu-click="reset">Reset</button>
</div>
HTML
end
on_event "increment" do
@history << @count
@count += 1
push_state
end
on_event "decrement" do
@history << @count
@count -= 1
push_state
end
on_event "reset" do
@history.clear
@count = 0
push_state
end
endAzu::Spark.register(MyComponent)
Azu::Spark.register(CounterComponent)
Azu::Spark.register(UserComponent)<script src="/azu/spark.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
Spark.connect('/spark');
});
</script><div azu-component="CounterComponent"></div>
<div azu-component="UserComponent" azu-props='{"user_id": 42}'></div>class TodoComponent
include Azu::Component
@todos = [] of Todo
@new_todo = ""
def mount(socket)
@todos = Todo.all
push_state
end
def content
<<-HTML
<div class="todo-app">
<h1>Todos (#{@todos.size})</h1>
<form azu-submit="add_todo">
<input type="text"
azu-model="new_todo"
value="#{@new_todo}"
placeholder="What needs to be done?">
<button type="submit">Add</button>
</form>
<ul id="todo-list">
#{@todos.map { |t| render_todo(t) }.join}
</ul>
</div>
HTML
end
private def render_todo(todo : Todo)
<<-HTML
<li id="todo-#{todo.id}" class="#{todo.completed? ? "completed" : ""}">
<input type="checkbox"
azu-change="toggle"
azu-value="#{todo.id}"
#{todo.completed? ? "checked" : ""}>
<span>#{todo.title}</span>
<button azu-click="delete" azu-value="#{todo.id}">×</button>
</li>
HTML
end
on_event "new_todo_change" do |value|
@new_todo = value.as_s
end
on_event "add_todo" do
unless @new_todo.empty?
todo = Todo.create!(title: @new_todo)
@todos << todo
@new_todo = ""
push_state
end
end
on_event "toggle" do |id|
if todo = @todos.find { |t| t.id == id.as_i64 }
todo.toggle!
push_replace("#todo-#{todo.id}", render_todo(todo))
end
end
on_event "delete" do |id|
@todos.reject! { |t| t.id == id.as_i64 }
push_remove("#todo-#{id}")
end
end