live
Live-updating templates driven by MQTT topics.
Note
This scomp is provided by module#mod_mqtt, which must be enabled.
This tag renders templates that are automatically re-rendered after a publication to an MQTT topic.
Example
An example of a template showing the newest content of a resource:
{% live template="_detail.tpl" topic=id id=id %}
This renders the template _detail.tpl. If the resource with id id is updated then the template will be replaced with
a freshly rendered template.
The tag can subscribe to multiple topics at once.
Add the argument catinclude to do a tag#catinclude instead of a normal
tag#include. For a catinclude the argument id must be present:
{% live template="_detail.tpl" topic=id catinclude id=id %}
Arguments
Use either template to render a template or, without template, supply
postback, delegate, and target to call an event handler.
| Argument | Default | Description |
|---|---|---|
topic | None | MQTT topic to subscribe to. Repeat this argument for multiple topics. Accepts a topic string, a list of topic segments, a resource id, or an edge-topic tuple; see Live topics below. |
template | None | Template to render on notifications. When present, selects template mode. |
catinclude | false | Select a category-specific version of template, using the resource passed as id. |
id | None | Template variable identifying the resource. Required when using catinclude; does not automatically subscribe to that resource's topic. |
target | Generated element id in template mode | DOM element id to update, without #. When supplied, no wrapper is generated: the caller must provide the target element. Required in postback mode. |
element | div | HTML tag for the generated wrapper in template mode. Ignored when target is supplied. An empty string suppresses the wrapper; the template must then provide an element with the generated target id. |
method | update in template mode | How to insert or update rendered HTML; see Update methods below. In postback mode the delegate handles rendering. |
postback | None | Message delivered in #postback{message = Message, target = Target} to the delegate. Required when template is omitted. |
delegate | None | Module implementing event/2 for the postback. Required when template is omitted. |
throttle | 0 | Non-negative integer interval in milliseconds between refreshes during a burst. 0 disables throttling; see Throttling updates below. |
In template mode, arguments other than topic, template, catinclude,
element, method, and throttle are passed as template variables. Pass any
required variables explicitly: the surrounding template's variables are not
automatically inherited. The scomp also supplies target and is_live_update.
The latter is false for the initial render and true for notification-driven
renders.
Update methods
| Method | Initial render | On notification |
|---|---|---|
update | Render the template at the tag's location. | Replace the target's contents. |
updateonly | No template render. | Replace the target's contents. |
top | No template render. | Prepend rendered HTML inside the target. |
bottom | No template render. | Append rendered HTML inside the target. |
before | No template render. | Insert rendered HTML before the target. |
after | No template render. | Insert rendered HTML after the target. |
patch | No template render. | Update the target through Cotonic's UI model. |
Without an explicit target, a wrapper is generated even for methods that do
not render the template initially. With an explicit target, place the live
tag inside that element if using update and its initial render is wanted there.
For example, append a rendered item to an existing list on each notification:
<ul id="{{ #items }}"></ul>
{% live topic="bridge/origin/public/items"
template="_item.tpl" target=#items method="bottom"
%}
In notification-driven template renders, a map or proplist MQTT payload is
available as query arguments (q). Other payload values are available as
q.payload. These values are untrusted input: escape them when outputting HTML.
To handle notifications in Erlang instead of rendering a template:
<div id="{{ #status }}"></div>
{% live topic="bridge/origin/public/status"
postback={refresh_status id=id}
delegate="mod_example" target=#status throttle=3000
%}
The delegate receives the configured message and target, and can read the
notification's topic and MQTT message using z_context:get_q/2 with the binary
keys topic and message. Extra live-tag arguments are not automatically added
to the postback message; include them in postback={...} explicitly. The target
must remain in the DOM for the subscription to stay active.
Throttling updates
Use throttle=3000 to limit refreshes during a burst to once every three seconds.
The first notification refreshes after a short delay (at most 100 milliseconds),
then the full interval starts from that refresh. After no notifications for a
full interval, the next notification refreshes quickly again. Notifications are
combined using the latest topic and message. Continuous events do not postpone
the refresh, and the last event is included even if events stop.
Initial rendering is unchanged. The default is 0 (no throttling).
The interval is shared by all topics on one live tag. Separate live tags
have independent intervals. The argument applies to both template rendering
and the postback/delegate form of the live tag; it is not passed to the
rendered template as a variable. Throttling combines browser refresh requests,
not the MQTT publications themselves.
Use this for templates showing current state, not event-by-event inserts:
{% live template="_detail.tpl" topic=id id=id throttle=3000 %}
Live topics
Any MQTT topic can be used. The topics are interpreted as local to the page. There are three special topics:
- Use any integer to map to the resource’s update topic. For example if id is
1234then the topic will bebridge/origin/model/rsc/event/1234 - Use the tuple
{object id=...}to listen to changes of outgoing connections from a page. An example of a mapped topic is bridge/origin/model/edge/event/1234/o/+`. Use the tuple{object id=... predicate=...}to listen to changes of a specific predicate of a page. An example of a mapped topic isbridge/origin/model/edge/event/1234/o/author - Use the tuple
{subject id=... }to listen to changes of incoming connections to a page. An example of a mapped topic isbridge/origin/model/edge/event/1234/s/author
Note that the topics refer to client side topics, that is why the bridge is used to subscribe to server side model events.
It is possible to subscribe to client topics like "my/local/topic" and have the actions triggered by publish to
cotonic.broker.publish("my/local/topic", {}); (with any payload).
Live actions
It is possible to wire actions or postbacks to a MQTT topic.
Use the scomp#wire with argument type={mqtt topic=... topic=...} to
connect to one or more MQTT topics. Add throttle inside type={mqtt ...}
to combine rapid notifications before executing the wire's actions and postback.
It uses the same millisecond interval, quick first event, and idle reset as the
live tag. All topics of one wire share an interval; separate wires are independent.
The latest notification supplies the event arguments. Omit throttle or use 0
for immediate execution of every notification:
{% wire type={mqtt topic="bridge/origin/public/hello" throttle=3000}
action={growl text="hello"}
%}
And in Erlang this will trigger the above growl:
z_mqtt:publish(<<"public/hello">>, <<>>, z:c(mysite)).