class Element

Represents a single dynamic content area on the page.

Definitions

def self.unique_id

Generate a unique identifier for an element.

Signature

returns String

The generated identifier.

Implementation

def self.unique_id
	SecureRandom.uuid
end

def self.root(id = self.unique_id, data: {}, **options)

Create a new root element with a convenient syntax for specifying the id and data.

Signature

parameter id String

The unique identifier within the page.

parameter data Hash

The data associated with the element, typically stored as data- attributes.

parameter options Hash

Additional options passed to the element constructor.

Implementation

def self.root(id = self.unique_id, data: {}, **options)
	self.new(id, data, **options)
end

def self.child(parent, id = self.unique_id, data: {}, **options)

Mount an element within a parent element.

Signature

parameter parent Element

The parent element.

parameter id String

The unique identifier within the parent element.

parameter data Hash

The data associated with the element, typically stored as data- attributes.

parameter options Hash

Additional options passed to the element constructor.

Implementation

def self.child(parent, id = self.unique_id, data: {}, **options)
	full_id = parent.id + ":" + id
	
	self.new(full_id, data, **options)
end

def self.mount(parent, id, data: {}, **options)

Mount an element within a parent element.

Signature

parameter parent Element

The parent element.

parameter id String

The unique identifier within the parent element.

parameter data Hash

The data associated with the element, typically stored as data- attributes.

parameter options Hash

Additional options passed to the element constructor.

Implementation

def self.mount(parent, id, data: {}, **options)
	self.child(parent, id, data:, **options)
end

def initialize(id, data)

Initialize the element with the specified id and data.

Signature

parameter id String

The unique identifier within the page.

parameter data Hash

The data associated with the element, typically stored as data- attributes.

Implementation

def initialize(id, data)
	data[:class] ||= self.class.name
	
	@id = id
	@data = data
	@page = nil
end

attr :id

The unique id within the bound page.

attr :data

The data associated with the element.

attr :page

Signature

attribute Page | Nil

The page this element is bound to.

def selector

A CSS selector that uniquely identifies this element.

Signature

returns String

Implementation

def selector
	"[id=#{JSON.dump(@id)}]"
end

def forward_event(detail = nil)

Generate a JavaScript string which forwards the specified event to the server.

Signature

parameter detail Hash

The detail associated with the forwarded event.

Implementation

def forward_event(detail = nil)
	if detail
		"live.forwardEvent(#{JSON.dump(@id)}, event, #{JSON.dump(detail)})"
	else
		"live.forwardEvent(#{JSON.dump(@id)}, event)"
	end
end

def forward_form_event(detail = nil)

Generate JavaScript which forwards a form event and its form data to the server.

Signature

parameter detail Hash | Nil

Additional detail associated with the forwarded event.

returns String

The generated JavaScript expression.

Implementation

def forward_form_event(detail = nil)
	if detail
		"live.forwardFormEvent(#{JSON.dump(@id)}, event, #{JSON.dump(detail)})"
	else
		"live.forwardFormEvent(#{JSON.dump(@id)}, event)"
	end
end

def bind(page)

Bind this tag to a dynamically updating page.

Signature

parameter page Live::Page

Implementation

def bind(page)
	@page = page
end

def close

Detach the element from its page.

Implementation

def close
	@page = nil
end

def handle(event)

Handle a client event, typically as triggered by #forward_event.

Signature

parameter event String

The type of the event.

Implementation

def handle(event)
end

def rpc(*arguments)

Enqueue a remote procedure call to the currently bound page.

Signature

parameter method Symbol

The name of the remote function to invoke.

parameter arguments Array

Implementation

def rpc(*arguments)
	if @page
		# This update might not be sent right away. Therefore, mutable arguments may be serialized to JSON at a later time (or never). This could be a race condition:
		@page.enqueue(arguments)
	else
		# This is a programming error, as it probably means the element is still part of the logic of the server side (e.g. async loop), but it is not bound to a page, so there is nothing to update/access/rpc.
		raise PageError, "Element is not bound to a page, make sure to implement #close!"
	end
end

def script(code, **options)

Execute JavaScript in the context of the client-side element.

Signature

parameter code String

The JavaScript source code to execute.

parameter options Hash

Options for the remote procedure call.

Implementation

def script(code, **options)
	rpc(:script, @id, code, options)
end

def update!(**options)

Update the content of the client-side element by rendering this view.

Implementation

def update!(**options)
	rpc(:update, @id, self.to_html, options)
end

def replace(selector, fragment = nil, **options, &block)

Replace the content of the client-side element by rendering this view.

Signature

parameter selector String

The CSS selector to replace.

parameter node String

The HTML to replace.

Implementation

def replace(selector, fragment = nil, **options, &block)
	fragment ||= XRB::Builder.fragment(&block)
	
	rpc(:replace, selector, fragment.to_s, options)
end

def prepend(selector, fragment = nil, **options, &block)

Prepend to the content of the client-side element by appending the specified element.

Signature

parameter selector String

The CSS selector to prepend to.

parameter node String

The HTML to prepend.

Implementation

def prepend(selector, fragment = nil, **options, &block)
	fragment ||= XRB::Builder.fragment(&block)
	
	rpc(:prepend, selector, fragment.to_s, options)
end

def append(selector, fragment = nil, **options, &block)

Append to the content of the client-side element by appending the specified element.

Signature

parameter selector String

The CSS selector to append to.

parameter node String

The HTML to prepend.

Implementation

def append(selector, fragment = nil, **options, &block)
	fragment ||= XRB::Builder.fragment(&block)
	
	rpc(:append, selector, fragment.to_s, options)
end

def remove(selector, **options)

Remove the specified element from the client-side element.

Signature

parameter selector String

The CSS selector to remove.

Implementation

def remove(selector, **options)
	rpc(:remove, selector, options)
end

def dispatch_event(selector, type, **options)

Dispatch an event to each client-side element matching the selector.

Signature

parameter selector String

The CSS selector for the target elements.

parameter type String

The event type to dispatch.

parameter options Hash

The event initialization options.

Implementation

def dispatch_event(selector, type, **options)
	rpc(:dispatchEvent, selector, type, options)
end

def render(builder)

Render the element.

Signature

parameter builder XRB::Builder

The HTML builder.

Implementation

def render(builder)
	builder.text(self.class.name)
end

def append_markup(output)

Append this element's markup to the specified output buffer.

Signature

parameter output Object

The output buffer which receives the markup.

Implementation

def append_markup(output)
	build_markup(::XRB::Builder.new(output))
end

def build_markup(builder)

Build this element's markup with the specified builder.

Signature

parameter builder XRB::Builder

The builder which receives the markup.

Implementation

def build_markup(builder)
	render(builder)
end

def to_html

Signature

returns Object

The generated HTML.

Implementation

def to_html
	XRB::Builder.fragment(&self.method(:build_markup))
end

def to_s

Convenience method for rendering the view as a string.

Signature

returns String

The generated HTML.

Implementation

def to_s
	to_html.to_s
end