{"result":{"depiction_url":null,"edges":{"subject":{"objects":[{"created":"2026-09-30T09:11:23Z","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-30T09:11:23Z","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-30T09:11:23Z","object_id":{"id":2583,"is_a":["categorization","keyword","keyword_domain"],"name":"zotonic_topic_user_interface_and_interaction","title":"User interface and interaction","uri":"https:\/\/zotonic.com\/id\/2583"},"seq":3},{"created":"2026-09-30T09:11:23Z","object_id":{"id":2625,"is_a":["categorization","keyword","keyword_architecture"],"name":"zotonic_topic_model","title":"Model","uri":"https:\/\/zotonic.com\/id\/2625"},"seq":4},{"created":"2026-09-30T09:11:23Z","object_id":{"id":2673,"is_a":["categorization","keyword","keyword_technology"],"name":"zotonic_topic_javascript","title":"JavaScript","uri":"https:\/\/zotonic.com\/id\/2673"},"seq":5},{"created":"2026-09-30T09:11:23Z","object_id":{"id":2718,"is_a":["categorization","keyword","keyword_technology"],"name":"zotonic_topic_cotonic","title":"Cotonic","uri":"https:\/\/zotonic.com\/id\/2718"},"seq":6},{"created":"2026-09-30T09:11:23Z","object_id":{"id":2721,"is_a":["categorization","keyword","keyword_technology"],"name":"zotonic_topic_dom","title":"Document Object Model","uri":"https:\/\/zotonic.com\/id\/2721"},"seq":7}],"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":2737,"is_a":["text","documentation","reference","cotonic_reference","cotonic_model"],"links":[{"rel":"self","target":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/2737"},{"rel":"hub","target":"https:\/\/zotonic.com\/.zotonic\/websub"}],"medium":null,"medium_url":null,"name":"doc_cotonic_model_ui","page_url":{"en":"https:\/\/zotonic.com\/cotonic\/models\/ui","x-default":"https:\/\/zotonic.com\/cotonic\/models\/ui"},"preview_url":null,"resource":{"version":15,"pivot_location_lat":null,"title":"model\/ui","is_authoritative":true,"body":"<a name=\"model.ui\"><\/a><h3>model\/ui<\/h3>\n\n            <p>\n            The ui model can be used to update the dom by publishing messages on one of the\n            <tt>model\/ui\/#<\/tt> topics. Elements can be inserted, deleted and updated. If\n            elements in the dom-tree are updated, then a message\n            <tt>model\/ui\/event\/dom-updated\/+key<\/tt> is published. This can be used to\n            react to dom-changes when they happen.\n            <\/p>\n\n            <a name=\"model.ui.insert.key\"><\/a><p>\n            <strong class=\"header\">insert\/+key<\/strong>\n            <br \/>\n            Insert a new ui snippet named <tt>key<\/tt> into the user interface composer.\n            Expects a message with the following properties.\n\n            <dl>\n                <dt><tt>initialData<\/tt><\/dt>\n                <dd>The initial data to place in the dom tree.<\/dd>\n\n                <dt><tt>inner<\/tt><\/dt>\n                <dd>If set to <tt>true<\/tt>, the ui composer will update the inner html, when set\n                to <tt>false<\/tt>, the outer html<\/dd>\n\n                <dt><tt>priority<\/tt><\/dt>\n                <dd>An element with a high priority will be rendered before elements with a lower\n                priority.\n                <!-- needs example -->\n                <\/dd>\n            <\/dl>\n\n            <\/p>\n            <pre>\nself.publish(&quot;model\/ui\/insert\/root&quot;, {\n    initialData: &quot;&lt;div class=&#39;loading&#39;&gt;Loading&lt;\/div&gt;&quot;,\n    inner: true\n    priority: 100\n}, {retain: true});\n=&gt; When the ui composer receives this message it will add the\n   loading\n            <\/pre>\n\n            <a name=\"model.ui.update.key\"><\/a><p>\n            <strong class=\"header\">update\/+key<\/strong>\n            <br \/>\n            Update the contents of ui snippet named <tt>key<\/tt> previously registered\n            by an <tt>insert<\/tt>. Expects a message with the html snippet as message.\n            <\/p>\n            <pre>\nself.publish(&quot;model\/ui\/update\/root&quot;,\n    &quot;<div class=\"main-app&gt;\"><\/div>&quot;);\n=&gt; The root element is updated.<\/pre>\n\n            <a name=\"model.ui.delete.key\"><\/a><p>\n            <strong class=\"header\">delete\/+key<\/strong>\n            <br \/>\n            Delete ui snippet named <tt>key<\/tt> from the user interface composer.\n            <\/p>\n            <pre>\nself.publish(&quot;model\/ui\/delete\/root&quot;);\n=&gt; The root element is removed from the composer, but\n   stays in the DOM tree.<\/pre>\n\n            <a name=\"model.ui.render\"><\/a><p>\n            <strong class=\"header\">render<\/strong>\n            <br \/>\n            Trigger a render of all registered elements.\n            <\/p>\n            <pre>\nself.publish(&quot;model\/ui\/render&quot;);<\/pre>\n\n            <a name=\"model.ui.render.key\"><\/a><p>\n            <strong class=\"header\">render\/+key<\/strong>\n            <br \/>\n            Trigger a render of the element <tt>key<\/tt>\n            <\/p>\n            <pre>\nself.publish(&quot;model\/ui\/render.root&quot;);<\/pre>\n            \n\n            <a name=\"model.ui.render-template.key\"><\/a><p>\n            <strong class=\"header\">render-template\/+key<\/strong>\n            <br \/>\n            Request rendering a template with parameters. A rendering engine must be\n            available to respond to the render requests. The render result will update\n            <tt>key<\/tt> element. Note: The element must first be inserted with\n            <a href=\"\/id\/doc_cotonic_cotonic_ui#cotonic.ui.insert\">cotonic.ui.insert<\/a> or\n            <a href=\"\/id\/doc_cotonic_model_ui#model.ui.insert.key\"><tt>model\/ui\/insert\/+key<\/tt><\/a>.\n            The payload of the render request should be:\n            <dl>\n                <dt><tt>topic<\/tt><\/dt>\n                <dd>The topic on which the render engine is subscribed. Can be a local or a remote topic.\n                The response of the renderer will be updated on <tt>key<\/tt><\/dd>\n\n                <dt><tt>data<\/tt><\/dt>\n                <dd>The data which the render engine can use to render the template.<\/dd>\n\n                <dt><tt>dedup<\/tt><\/dt>\n                <dd>Send the render request via a MQTT QoS 1 message. This means that we can\n                get multiple responses. Dedup makes sure the result will be rendered only once.<\/dd>\n            <\/dl>\n            <\/p>\n            <pre>\nself.publish(&quot;model\/ui\/render-template\/root&quot;,\n             {topic: &quot;bridge\/origin\/model\/template\/get\/render\/hello.tpl&quot;,\n              dedup: true,\n              data: {name: &quot;Bob&quot;}\n             });\n==&gt; The render engine listening at bridge\/origin\/model\/template receives\n    a render request. The render result will be placed in element root.<\/pre>\n\n            <a name=\"model.ui.replace.key\"><\/a><p>\n            <strong class=\"header\">replace\/+key<\/strong>\n            <br \/>\n            Replace an element with id <tt>key<\/tt> with a new element or elements. If the\n            new HTML is passed as a string then the new DOM tree can have multiple elements.<br \/>\n            The replacement is done once and no further updates are registered.\n            <\/p>\n            <pre>\nself.publish(&quot;model\/ui\/replace\/eltid&quot;, &quot;Hello <b>and here we are<\/b>&quot;);\n            <\/pre>\n\n            <a name=\"model.ui.event.recent-activity\"><\/a><p>\n            <strong class=\"header\">event\/recent-activity<\/strong>\n            <br \/>\n            A message containing the user&#39;s activity status is published regularly\n            on this topic. The message is an object which contains a\n            <tt>is_active<\/tt> boolean property which indicates if the has been\n            active over the last period between publishes.\n            <\/p>\n            <pre>\n\/\/ worker example\nself.subscribe(&quot;model\/ui\/event\/recent-activity&quot;,\n    function(m) {\n        const a = m.payload.is_active?&quot;active&quot;:&quot;not active&quot;;\n        console.log(&quot;The user is: &quot;, a);\n    }\n);\n=&gt; Displays the user&#39;s activity status.<\/pre>\n\n            <a name=\"model.ui.event.dom-updated\"><\/a><p>\n            <strong class=\"header\">event\/dom-updated\/+key<\/strong>\n            <br \/>\n            When the dom-tree is updated a message will be published. The\n            message is fired after the dom-tree is updated.\n            <\/p>\n            <pre>\ncotonic.broker.subscribe(&quot;model\/ui\/event\/dom-updated\/message-container&quot;,\n    function(m) {\n        const c = document.getElementById(&quot;message-container&quot;);\n        c.scrollTop = c.scrollHeight\n    }\n);\n=&gt; Scrolls the messages bubble container to the bottom.\n            <\/pre>\n\n            <!-- ui status -->\n\n            <a name=\"model.ui.event.ui-status\"><\/a><p>\n            <strong class=\"header\">event\/+model\/ui-status<\/strong>\n            <br \/>\n            Events send to this topic will result in class and data attributes set on\n            the <tt>html<\/tt> of the document. The <tt>model<\/tt> attribute is used in the\n            data and class attributes. This can be used to signal the state of a model to\n            the document. This can be used to signal the state of cotonic workers or models to\n            other ui libraries like <a href=\"https:\/\/elm-lang.org\">Elm<\/a> or\n            <a href=\"https:\/\/reactjs.org\">React<\/a>. The payload of the message:\n            <dl>\n                <dt><tt>status<\/tt><\/dt>\n                <dd>Object with model statuses. Will be set as data attributes.<\/dd>\n                <dt><tt>classes<\/tt><\/dt>\n                <dd>List with model statuses. Will be set as classes.<\/dd>\n            <\/dl>\n            <\/p>\n            <pre>\nself.publish(&quot;model\/auth\/event\/ui-status&quot;,  {status: {&quot;auth&quot;: &quot;anonymous&quot;},\n                                             classes: [&quot;anonymous&quot;]});\n==&gt; model.ui adds clas &quot;ui-state-auth-anonymous&quot; and\n    adds data-ui-state-auth-auth=&quot;anonymous&quot; to the html element of the document.<\/pre>\n\n            <!-- event\/new-shadow-root\/+key -->\n\n            <a name=\"model.ui.event.new-shadow-root.key\"><\/a><p>\n            <strong class=\"header\">event\/new-shadow-root\/+key<\/strong>\n            <br \/>\n            Init the topic event listener when new shadow roots are added.\n            <dl>\n                <dt><tt>id<\/tt><\/dt>\n                <dd>Element id<\/dd>\n            <\/dl>\n            <\/p>\n            <pre>\ncotonic.broker.subscribe(&quot;model\/ui\/event\/new-shadow-root\/data-list&quot;,\n    function(m) {\n         console.log(&quot;Init the event listener for&quot;, &quot;data-list&quot;)\n    }\n);\n=&gt; Init the topic event listener when new shadow roots are added.<\/pre>\n\n            <!-- lifecycle model -->\n            ","slug":"model-ui","is_protected":false,"visible_for":0,"tz":"UTC","language":["en"],"doc_source_hash":"6610c093ae371abfc26ddb002792786e3fe4094cbbe518b0e6f61218aca02e05","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":2723,"is_a":["meta","category"],"name":"cotonic_model","title":"Cotonic models","uri":"https:\/\/zotonic.com\/id\/cotonic_model"},"doc_source_path":"index.html","publication_start":"2026-09-30T09:11:23Z","page_path":"\/cotonic\/models\/ui","github_url":"https:\/\/github.com\/cotonic\/cotonic\/blob\/master\/index.html","pivot_location_lng":null,"doc_source_kind":"cotonic","name":"doc_cotonic_model_ui","is_unfindable":false,"is_published":true,"pivot_geocode":null,"created":"2026-09-30T09:11:23Z","uri":null,"doc_source_anchor":"model.ui","doc_status":"current","is_dependent":false,"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":"694f9f9fd2e5c3e95c094eedbead6f844e243660","creator_id":{"id":1,"is_a":["person"],"name":"administrator","title":"Site Administrator","uri":"https:\/\/zotonic.com\/id\/1"},"modified":"2026-09-30T09:11:24Z","title_slug":"model-ui"},"uri":"https:\/\/zotonic.com\/id\/2737","uri_template":"https:\/\/zotonic.com\/id\/:id","websub":{"hub":"https:\/\/zotonic.com\/.zotonic\/websub","topic":"https:\/\/zotonic.com\/.zotonic\/websub\/topic\/2737"}},"status":"ok"}