Live-updating, server-rendered views for Zotonic β powered by MQTT and Cotonic.
zotonic_mod_teleview lets you embed server-rendered HTML fragments in a Zotonic page that stay automatically up to date β without full-page reloads. The server pushes incremental DOM patches over MQTT; the client applies them seamlessly, just like a video stream for your UI.
- Server-side rendering with live DOM updates β no full page reloads; only the changed parts of the DOM are updated.
- Video-like streaming updates via MQTT β keyframe, cumulative, and incremental patches keep bandwidth minimal and clients in sync.
- Per-renderer ACL, language, and user-specific variation via
varyβ serve different views to admins, visitors, or individual users from a single teleview. - Automatic renderer lifecycle management β renderers start on demand when a client subscribes and stop automatically when no viewers remain.
- Built on Zotonic + Cotonic β works seamlessly within the Zotonic ecosystem using the standard MQTT broker already present on every page.
The client side teleview component is built with Cotonic. This is a JavaScript library which acts as middleware. Different components can be added. These components can then communicate by using its broker. The broker provides a MQTT publish/subscribe model. Cotonic makes it possible for a page to connect to multiple MQTT brokers. Zotonic uses the Cotonic library. Normally a page opens one MQTT connection to the server from which the page was loaded. This is called the origin.
Teleview runs as a Zotonic module.
Each teleview has its own id and consists of a state process, which manages the subscription to the topics it listens to, and one or more renderer processes which can hold private vary properties used for rendering.
Zotonic High Level Architecture
Anatomy of a Zotonic Module
Teleview Module
- A running Zotonic installation.
zotonic_mod_televiewenabled for your site.
Put a {% teleview %} scomp into your template:
{% teleview
type = "example"
template = "_example_teleview.tpl"
topic = "model/example/event/#"
extra_parameter_a = 100
extra_parameter_b = "hello world"
vary = %{ user_id = m.acl.user }
%}
When this scomp is placed on a page, it ensures the teleview and renderer are started. A teleview can have multiple renderers depending on the situation β a different renderer can be started because of authorization levels, giving different admin and visitor views, or different views per user id, language, and other possibilities.
The scomp sends a notification ensure_teleview. An observer can then make sure a teleview process and
a renderer is started. When the renderer is already started, the current renderer state will be added to the
HTML output of the teleview scomp.
A piece of JavaScript code will subscribe itself to the update topic of the teleview renderer. This will keep the view updated.
The full scomp signature is:
{% teleview
type = "<type-name>"
template = "_a_template.tpl"
topic = <an mqtt topic to listen to>
teleview_param = ...
vary = %{
renderer_param: x
...
}
%}
Implement the following notifications in your Zotonic module to control the context used for state management and rendering:
observe_teleview_state_init({teleview_state_init, #{ type := <<"example">> }=Args}, Context) ->
%% Return a context which is allowed to subscribe the topics.
Context;
observe_teleview_state_init(_InitArgs, _Context) ->
undefined.
observe_teleview_renderer_init({teleview_renderer_init, #{ type := <<"example">> }=Args}, Context) ->
%% Return a context which allows rendering things.
Context;
observe_teleview_renderer_init(_InitArgs, _Context) ->
undefined.Each renderer can have different parameters so they can render the interface in different languages, for different users, or with a special set of ACL settings.
Returns the current state of a renderer of a teleview.
A teleview with id id was started.
A teleview with id id was stopped.
The teleview was reset, possibly because of a crash. It was restarted, meaning that the current state of the teleview viewer has to be reset.
A renderer will send still-watching messages to its viewers. When it does not
receive a response, the renderer assumes that nobody is interested in updates,
and will stop itself. When a teleview has no more renderers, it will be stopped
too.
The topic on which a renderer broadcasts its update.
| patch_type | description |
|---|---|
| keyframe | A complete rendered tree element. |
| cumulative | A diff against the last keyframe. |
| incremental | A diff against the last frame. |
A keyframe is the complete, text representation of a teleview. The view can be made visible by a teleview viewport on the client side by placing it in the DOM-tree.
{
"sn": <serial-number>,
"frame": <text>
}A cumulative update is a patch against the last keyframe of a teleview. It is short for cumulative patch. The view of this frame can be made visible by applying the patch against the text representation of the keyframe with the specified serial number. It will result in a new text representation which can be placed in the DOM-tree. The patched keyframe document is the current frame.
Note Because the patch is made against the keyframe it is possible to skip cumulative updates when the client is too busy. The view will then not be updated. The next cumulative patch can be used to update the view.
{
"sn": <serial>,
"patch": <patch>
}An incremental update is a patch against the current frame. The view of this frame can be made visible by applying the patch against the text representation of the current frame, and placing it in the DOM-tree. After applying the patch, the resulting text representation will be the new current frame.
Note Incremental updates must be applied; when they are skipped, it will not be possible to construct a new current frame by applying patches. The teleview viewer will have to wait for a new keyframe. Because of this drawback, teleview renderer will try to send cumulative updates as often as possible. Only when the patch against the keyframe becomes too complex, or the minimum time between keyframes is passed, an incremental patch will be sent.
{
"sn": <timestamp>,
"patch": <patch>
}The diff is a list of instructions.
- "c" for copy
- "s" for skip
- "i" for insert
Example:
["c", 100, "s", 51, "i", "<b>Hello World</b>", "c", 57]Which means, copy 100 characters from the current document to the new, skip 51 characters from the current document, insert "Hello World", and copy 57 characters from the current document to the new one.
The teleview Zotonic module is a supervisor. It can start televiews. Televiews are a process which manages the state, and a collection of renderers. The renderers take care of producing a render. Each renderer can have its own ACL and language settings. The state process selects the right renderer for the client side of the teleview.





