{"result":{"depiction_url":null,"edges":{"in_module":{"objects":[{"created":"2020-05-30T05:47:18Z","object_id":{"id":1324,"is_a":["text","documentation","reference","module"],"name":"doc_module_mod_mqtt","title":"mod_mqtt","uri":"https:\/\/zotonic.com\/id\/1324"},"seq":1000000}],"predicate":{"id":333,"is_a":["meta","predicate"],"name":"in_module","title":{"_type":"trans","tr":{"en":"In module"}},"uri":"https:\/\/zotonic.com\/id\/in_module"}},"references":{"objects":[{"created":"2020-05-30T05:47:18Z","object_id":{"id":1324,"is_a":["text","documentation","reference","module"],"name":"doc_module_mod_mqtt","title":"mod_mqtt","uri":"https:\/\/zotonic.com\/id\/1324"},"seq":1000000},{"created":"2020-05-30T05:47:18Z","object_id":{"id":1325,"is_a":["text","documentation","reference","template_tag"],"name":"doc_template_tag_tag_catinclude","title":"catinclude","uri":"https:\/\/zotonic.com\/id\/1325"},"seq":1000000},{"created":"2020-05-30T05:47:18Z","object_id":{"id":1326,"is_a":["text","documentation","reference","template_tag"],"name":"doc_template_tag_tag_include","title":"include","uri":"https:\/\/zotonic.com\/id\/1326"},"seq":1000000},{"created":"2020-05-30T05:47:18Z","object_id":{"id":1327,"is_a":["text","documentation","reference","template_scomp"],"name":"doc_template_scomp_scomp_wire","title":"wire","uri":"https:\/\/zotonic.com\/id\/1327"},"seq":1000000}],"predicate":{"id":332,"is_a":["meta","predicate"],"name":"references","title":{"_type":"trans","tr":{"en":"References"}},"uri":"https:\/\/zotonic.com\/id\/references"}},"refers":{"objects":[{"created":"2026-09-09T13:48:07Z","object_id":{"id":1324,"is_a":["text","documentation","reference","module"],"name":"doc_module_mod_mqtt","title":"mod_mqtt","uri":"https:\/\/zotonic.com\/id\/1324"},"seq":1000000},{"created":"2026-09-09T13:48:07Z","object_id":{"id":1325,"is_a":["text","documentation","reference","template_tag"],"name":"doc_template_tag_tag_catinclude","title":"catinclude","uri":"https:\/\/zotonic.com\/id\/1325"},"seq":1000000},{"created":"2026-09-09T13:48:07Z","object_id":{"id":1326,"is_a":["text","documentation","reference","template_tag"],"name":"doc_template_tag_tag_include","title":"include","uri":"https:\/\/zotonic.com\/id\/1326"},"seq":1000000},{"created":"2026-09-09T13:48:07Z","object_id":{"id":1327,"is_a":["text","documentation","reference","template_scomp"],"name":"doc_template_scomp_scomp_wire","title":"wire","uri":"https:\/\/zotonic.com\/id\/1327"},"seq":1000000}],"predicate":{"id":2409,"is_a":["meta","predicate"],"name":"refers","title":{"_type":"trans","tr":{"en":"Refers"}},"uri":"https:\/\/zotonic.com\/id\/refers"}},"relation":{"objects":[{"created":"2020-05-30T05:47:18Z","object_id":{"id":1324,"is_a":["text","documentation","reference","module"],"name":"doc_module_mod_mqtt","title":"mod_mqtt","uri":"https:\/\/zotonic.com\/id\/1324"},"seq":1000000}],"predicate":{"id":303,"is_a":["meta","predicate"],"name":"relation","title":{"_type":"trans","tr":{"nl":"Relatie","en":"Relation"}},"uri":"http:\/\/purl.org\/dc\/terms\/relation"}},"subject":{"objects":[{"created":"2026-09-09T13:48:07Z","object_id":{"id":2555,"is_a":["categorization","keyword","keyword_information_type"],"name":"zotonic_topic_reference","title":"Reference","uri":"https:\/\/zotonic.com\/id\/2555"},"seq":1},{"created":"2026-09-09T13:48:07Z","object_id":{"id":2563,"is_a":["categorization","keyword","keyword_audience"],"name":"zotonic_topic_frontend_developer","title":"Frontend developer","uri":"https:\/\/zotonic.com\/id\/2563"},"seq":2},{"created":"2026-09-09T13:48:07Z","object_id":{"id":2598,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_messaging_and_pubsub","title":"Messaging and publish-subscribe","uri":"https:\/\/zotonic.com\/id\/2598"},"seq":3},{"created":"2026-09-09T13:48:07Z","object_id":{"id":2623,"is_a":["categorization","keyword","keyword_architecture"],"name":"zotonic_topic_scomp","title":"Server component","uri":"https:\/\/zotonic.com\/id\/2623"},"seq":4},{"created":"2026-09-09T13:48:07Z","object_id":{"id":2641,"is_a":["categorization","keyword","keyword_task"],"name":"zotonic_topic_render","title":"Render","uri":"https:\/\/zotonic.com\/id\/2641"},"seq":5},{"created":"2026-09-09T13:48:07Z","object_id":{"id":2677,"is_a":["categorization","keyword","keyword_technology"],"name":"zotonic_topic_mqtt","title":"MQTT","uri":"https:\/\/zotonic.com\/id\/2677"},"seq":6}],"predicate":{"id":308,"is_a":["meta","predicate"],"name":"subject","title":{"_type":"trans","tr":{"en":"Keyword"}},"uri":"http:\/\/purl.org\/dc\/elements\/1.1\/subject"}}},"id":1323,"is_a":["text","documentation","reference","template_scomp"],"links":[{"rel":"self","target":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/1323"},{"rel":"hub","target":"https:\/\/zotonic.com\/.zotonic\/websub"}],"medium":null,"medium_url":null,"name":"doc_template_scomp_scomp_live","page_url":{"en":"https:\/\/zotonic.com\/docs\/1323\/live","x-default":"https:\/\/zotonic.com\/docs\/1323\/live"},"preview_url":null,"resource":{"version":3991,"pivot_location_lat":null,"title":"live","is_authoritative":true,"body":"<p>Live-updating templates driven by MQTT topics.<\/p>\n<div class=\"admonition note\"><p class=\"first admonition-title\">Note<\/p><p class=\"last\">This scomp is provided by <a href=\"\/id\/doc_module_mod_mqtt\" class=\"doc-reference doc-reference-module\"><code>module#mod_mqtt<\/code><\/a>, which must be enabled.<\/p><\/div>\n<p>This tag renders templates that are automatically re-rendered after a publication to an MQTT topic.<\/p>\n<h2>Example<\/h2>\n<p>An example of a template showing the newest content of a resource:<\/p>\n<pre class=\"notranslate\"><code class=\"notranslate language-erlang\">{% live template=&quot;_detail.tpl&quot; topic=id id=id %}\n<\/code><\/pre>\n<p>This renders the template <code>_detail.tpl<\/code>. If the resource with id <code>id<\/code> is updated then the template will be replaced with\na freshly rendered template.<\/p>\n<p>The tag can subscribe to multiple topics at once.<\/p>\n<p>Add the argument <code>catinclude<\/code> to do a <a href=\"\/id\/doc_template_tag_tag_catinclude\" class=\"doc-reference doc-reference-tag\"><code>tag#catinclude<\/code><\/a> instead of a normal\n<a href=\"\/id\/doc_template_tag_tag_include\" class=\"doc-reference doc-reference-tag\"><code>tag#include<\/code><\/a>. For a catinclude the argument <code>id<\/code> must be present:<\/p>\n<pre class=\"notranslate\"><code class=\"notranslate language-erlang\">{% live template=&quot;_detail.tpl&quot; topic=id catinclude id=id %}\n<\/code><\/pre>\n<h2>Arguments<\/h2>\n<p>Use either <code>template<\/code> to render a template or, without <code>template<\/code>, supply\n<code>postback<\/code>, <code>delegate<\/code>, and <code>target<\/code> to call an event handler.<\/p>\n<table class=\"table\"><thead><tr><th>Argument<\/th><th>Default<\/th><th>Description<\/th><\/tr><\/thead><tbody><tr><td><code>topic<\/code><\/td><td>None<\/td><td>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.<\/td><\/tr><tr><td><code>template<\/code><\/td><td>None<\/td><td>Template to render on notifications. When present, selects template mode.<\/td><\/tr><tr><td><code>catinclude<\/code><\/td><td><code>false<\/code><\/td><td>Select a category-specific version of <code>template<\/code>, using the resource passed as <code>id<\/code>.<\/td><\/tr><tr><td><code>id<\/code><\/td><td>None<\/td><td>Template variable identifying the resource. Required when using <code>catinclude<\/code>; does not automatically subscribe to that resource&#39;s topic.<\/td><\/tr><tr><td><code>target<\/code><\/td><td>Generated element id in template mode<\/td><td>DOM element id to update, without <code>#<\/code>. When supplied, no wrapper is generated: the caller must provide the target element. Required in postback mode.<\/td><\/tr><tr><td><code>element<\/code><\/td><td><code>div<\/code><\/td><td>HTML tag for the generated wrapper in template mode. Ignored when <code>target<\/code> is supplied. An empty string suppresses the wrapper; the template must then provide an element with the generated <code>target<\/code> id.<\/td><\/tr><tr><td><code>method<\/code><\/td><td><code>update<\/code> in template mode<\/td><td>How to insert or update rendered HTML; see Update methods below. In postback mode the delegate handles rendering.<\/td><\/tr><tr><td><code>postback<\/code><\/td><td>None<\/td><td>Message delivered in <code>#postback{message = Message, target = Target}<\/code> to the delegate. Required when <code>template<\/code> is omitted.<\/td><\/tr><tr><td><code>delegate<\/code><\/td><td>None<\/td><td>Module implementing <code>event\/2<\/code> for the postback. Required when <code>template<\/code> is omitted.<\/td><\/tr><tr><td><code>throttle<\/code><\/td><td><code>0<\/code><\/td><td>Non-negative integer interval in milliseconds between refreshes during a burst. <code>0<\/code> disables throttling; see Throttling updates below.<\/td><\/tr><\/tbody><\/table>\n<p>In template mode, arguments other than <code>topic<\/code>, <code>template<\/code>, <code>catinclude<\/code>,\n<code>element<\/code>, <code>method<\/code>, and <code>throttle<\/code> are passed as template variables. Pass any\nrequired variables explicitly: the surrounding template&#39;s variables are not\nautomatically inherited. The scomp also supplies <code>target<\/code> and <code>is_live_update<\/code>.\nThe latter is <code>false<\/code> for the initial render and <code>true<\/code> for notification-driven\nrenders.<\/p>\n<h2>Update methods<\/h2>\n<table class=\"table\"><thead><tr><th>Method<\/th><th>Initial render<\/th><th>On notification<\/th><\/tr><\/thead><tbody><tr><td><code>update<\/code><\/td><td>Render the template at the tag&#39;s location.<\/td><td>Replace the target&#39;s contents.<\/td><\/tr><tr><td><code>updateonly<\/code><\/td><td>No template render.<\/td><td>Replace the target&#39;s contents.<\/td><\/tr><tr><td><code>top<\/code><\/td><td>No template render.<\/td><td>Prepend rendered HTML inside the target.<\/td><\/tr><tr><td><code>bottom<\/code><\/td><td>No template render.<\/td><td>Append rendered HTML inside the target.<\/td><\/tr><tr><td><code>before<\/code><\/td><td>No template render.<\/td><td>Insert rendered HTML before the target.<\/td><\/tr><tr><td><code>after<\/code><\/td><td>No template render.<\/td><td>Insert rendered HTML after the target.<\/td><\/tr><tr><td><code>patch<\/code><\/td><td>No template render.<\/td><td>Update the target through Cotonic&#39;s UI model.<\/td><\/tr><\/tbody><\/table>\n<p>Without an explicit <code>target<\/code>, a wrapper is generated even for methods that do\nnot render the template initially. With an explicit <code>target<\/code>, place the live\ntag inside that element if using <code>update<\/code> and its initial render is wanted there.<\/p>\n<p>For example, append a rendered item to an existing list on each notification:<\/p>\n<pre class=\"notranslate\"><code class=\"notranslate language-django\">&lt;ul id=&quot;{{ #items }}&quot;&gt;&lt;\/ul&gt;\n{% live topic=&quot;bridge\/origin\/public\/items&quot;\n        template=&quot;_item.tpl&quot; target=#items method=&quot;bottom&quot;\n%}\n<\/code><\/pre>\n<p>In notification-driven template renders, a map or proplist MQTT payload is\navailable as query arguments (<code>q<\/code>). Other payload values are available as\n<code>q.payload<\/code>. These values are untrusted input: escape them when outputting HTML.<\/p>\n<p>To handle notifications in Erlang instead of rendering a template:<\/p>\n<pre class=\"notranslate\"><code class=\"notranslate language-django\">&lt;div id=&quot;{{ #status }}&quot;&gt;&lt;\/div&gt;\n{% live topic=&quot;bridge\/origin\/public\/status&quot;\n        postback={refresh_status id=id}\n        delegate=&quot;mod_example&quot; target=#status throttle=3000\n%}\n<\/code><\/pre>\n<p>The delegate receives the configured message and target, and can read the\nnotification&#39;s topic and MQTT message using <code>z_context:get_q\/2<\/code> with the binary\nkeys <code>topic<\/code> and <code>message<\/code>. Extra live-tag arguments are not automatically added\nto the postback message; include them in <code>postback={...}<\/code> explicitly. The target\nmust remain in the DOM for the subscription to stay active.<\/p>\n<h2>Throttling updates<\/h2>\n<p>Use <code>throttle=3000<\/code> to limit refreshes during a burst to once every three seconds.\nThe first notification refreshes after a short delay (at most 100 milliseconds),\nthen the full interval starts from that refresh. After no notifications for a\nfull interval, the next notification refreshes quickly again. Notifications are\ncombined using the latest topic and message. Continuous events do not postpone\nthe refresh, and the last event is included even if events stop.\nInitial rendering is unchanged. The default is <code>0<\/code> (no throttling).<\/p>\n<p>The interval is shared by all topics on one <code>live<\/code> tag. Separate <code>live<\/code> tags\nhave independent intervals. The argument applies to both template rendering\nand the <code>postback<\/code>\/<code>delegate<\/code> form of the <code>live<\/code> tag; it is not passed to the\nrendered template as a variable. Throttling combines browser refresh requests,\nnot the MQTT publications themselves.<\/p>\n<p>Use this for templates showing current state, not event-by-event inserts:<\/p>\n<pre class=\"notranslate\"><code class=\"notranslate language-django\">{% live template=&quot;_detail.tpl&quot; topic=id id=id throttle=3000 %}\n<\/code><\/pre>\n<h2>Live topics<\/h2>\n<p>Any MQTT topic can be used. The topics are interpreted as local to the page. There are three special topics:<\/p>\n<ul><li>Use any integer to map to the resource’s update topic. For example if id is <code>1234<\/code> then the topic will be <code>bridge\/origin\/model\/rsc\/event\/1234<\/code><\/li><li>Use the tuple <code>{object id=...}<\/code> 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 <code>{object id=... predicate=...}<\/code> to listen to changes of a specific predicate of a page. An example of a mapped topic is <code>bridge\/origin\/model\/edge\/event\/1234\/o\/author<\/code><\/li><li>Use the tuple <code>{subject id=... }<\/code> to listen to changes of incoming connections to a page. An example of a mapped topic is <code>bridge\/origin\/model\/edge\/event\/1234\/s\/author<\/code><\/li><\/ul>\n<p>Note that the topics refer to <em>client side topics<\/em>, that is why the bridge is used to subscribe to server side model events.<\/p>\n<p>It is possible to subscribe to client topics like <code>&quot;my\/local\/topic&quot;<\/code> and have the actions triggered by publish to\n<code>cotonic.broker.publish(&quot;my\/local\/topic&quot;, {});<\/code> (with any payload).<\/p>\n<h2>Live actions<\/h2>\n<p>It is possible to wire actions or postbacks to a MQTT topic.<\/p>\n<p>Use the <a href=\"\/id\/doc_template_scomp_scomp_wire\" class=\"doc-reference doc-reference-scomp\"><code>scomp#wire<\/code><\/a> with argument <code>type={mqtt topic=... topic=...}<\/code> to\nconnect to one or more MQTT topics. Add <code>throttle<\/code> inside <code>type={mqtt ...}<\/code>\nto combine rapid notifications before executing the wire&#39;s actions and postback.\nIt uses the same millisecond interval, quick first event, and idle reset as the\nlive tag. All topics of one wire share an interval; separate wires are independent.\nThe latest notification supplies the event arguments. Omit <code>throttle<\/code> or use <code>0<\/code>\nfor immediate execution of every notification:<\/p>\n<pre class=\"notranslate\"><code class=\"notranslate language-django\">{% wire type={mqtt topic=&quot;bridge\/origin\/public\/hello&quot; throttle=3000}\n        action={growl text=&quot;hello&quot;}\n%}\n<\/code><\/pre>\n<p>And in Erlang this will trigger the above <em>growl<\/em>:<\/p>\n<pre class=\"notranslate\"><code class=\"notranslate language-erlang\">z_mqtt:publish(&lt;&lt;&quot;public\/hello&quot;&gt;&gt;, &lt;&lt;&gt;&gt;, z:c(mysite)).\n<\/code><\/pre>","slug":"live","is_protected":false,"visible_for":0,"tz":"UTC","language":["en"],"doc_source_hash":"332b9900a7ac9ef0a7839985367b2218364033b504e4459327a28699d6581b6b","is_featured":false,"content_group_id":{"id":2551,"is_a":["meta","content_group"],"name":"content_group_imported_docs","title":"Imported documentation","uri":"https:\/\/zotonic.com\/id\/content_group_imported_docs"},"category_id":{"id":327,"is_a":["meta","category"],"name":"template_scomp","title":"Scomp","uri":"https:\/\/zotonic.com\/id\/template_scomp"},"doc_source_path":"apps\/zotonic_mod_mqtt\/src\/scomps\/scomp_mqtt_live.erl","publication_start":"2023-06-24T08:17:51Z","github_url":"https:\/\/github.com\/zotonic\/zotonic\/blob\/master\/apps\/zotonic_mod_mqtt\/src\/scomps\/scomp_mqtt_live.erl","pivot_location_lng":null,"doc_source_kind":"scomp","name":"doc_template_scomp_scomp_live","is_unfindable":false,"is_published":true,"pivot_geocode":null,"created":"2020-05-30T05:47:18Z","uri":null,"doc_status":"current","is_dependent":false,"erlang_app":"zotonic_mod_mqtt","publication_end":"9999-06-01T00:00:00Z","modifier_id":{"id":1,"is_a":["person"],"name":"administrator","title":"Site Administrator","uri":"https:\/\/zotonic.com\/id\/1"},"privacy":0,"doc_source_commit":"fcd7360cf6d307f08dac71bede7378e2b4ac72f0\n","erlang_module":"scomp_mqtt_live","creator_id":{"id":336,"is_a":["person","robot"],"name":"gitbot","title":"Git","uri":"https:\/\/zotonic.com\/id\/336"},"modified":"2026-10-01T12:53:26Z","title_slug":"live"},"uri":"https:\/\/zotonic.com\/id\/1323","uri_template":"https:\/\/zotonic.com\/id\/:id","websub":{"hub":"https:\/\/zotonic.com\/.zotonic\/websub","topic":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/1323"}},"status":"ok"}