Some checks failed
CI / lint-and-test (push) Has been cancelled
Move content to correct locations: - AGENTS.md → .agents/AGENTS.md (with orchestrator reference update) - third_party/docs/ (8 reference docs) → wiki/concepts/ - manuals/ (12 Node-RED docs) → wiki/manuals/ Delete 23 unreferenced one-off scripts from scripts/ (keeping 5 active). Delete stale Dockerfile.e2e, docker-compose.e2e.yml, test/e2e/. Remove empty third_party/ directory. Root is now: README, CLAUDE.md, LICENSE, package.json, Makefile, Dockerfile, docker-compose.yml, docker/, scripts/ (5), nodes/, wiki/, plus dotfiles (.agents, .claude, .gitea). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.1 KiB
3.1 KiB
FlowFuse ui-template Manual (EVOLV Reference)
Source: https://dashboard.flowfuse.com/nodes/widgets/ui-template.html
Purpose
Custom Vue 3 / Vuetify / HTML widget. Full scripting, scoped CSS, send/receive messages.
Scopes
| Scope | Renders | Use Case |
|---|---|---|
| Widget (Group) | Inside a ui-group | Custom gauges, tables, controls |
| Widget (Page) | On page, outside groups | Floating overlays, full-width banners |
| Widget (UI) | On every page | Global headers, footers |
| CSS (All Pages) | N/A — injects <style> |
Global CSS overrides |
| CSS (Single Page) | N/A — injects <style> |
Page-specific CSS |
Properties
| Property | Type | Dynamic | Notes |
|---|---|---|---|
group |
ref | No | Parent ui-group (Group scope) |
page |
ref | No | Parent ui-page (Page scope) |
width |
int | No | Columns |
height |
int | No | Row units |
format |
string | Yes | Vue template string |
storeOutMessages |
bool | No | Persist last output for late joiners |
passthru |
bool | No | Forward input to output |
templateScope |
string | No | "widget:group", "widget:page", "widget:ui", "page:style", "site:style" |
Template Structure
<template>
<v-btn @click="send({payload: 'clicked'})">Click</v-btn>
<p>Value: {{ msg.payload }}</p>
</template>
<script>
export default {
data() { return { local: 0 }; },
watch: {
msg(val) { this.local = val.payload; }
},
methods: {
doSend() { this.send({payload: this.local}); }
},
mounted() { /* one-time setup */ },
unmounted() { /* cleanup */ }
};
</script>
<style scoped>
p { color: #2196f3; }
</style>
Built-in Variables
| Variable | Description |
|---|---|
id |
This node's unique ID |
msg |
Latest received message (reactive) |
$socket |
Socket.io client for custom events |
Sending Messages
this.send({ payload: 42, topic: "my-topic" });
this.submit(); // sends FormData from <form>
Receiving Messages
Option A — Vue watch:
watch: { msg(newMsg) { /* react */ } }
Option B — Socket listener:
mounted() {
this.$socket.on('msg-input:' + this.id, (msg) => { /* handle */ });
}
Teleports (inject into dashboard chrome)
<Teleport to="#app-bar-actions">
<v-btn>Custom Button</v-btn>
</Teleport>
Targets: #app-bar-title, #app-bar-actions.
Vuetify Components
All Vuetify 3 components are available without import: <v-btn>, <v-card>, <v-slider>, <v-data-table>, etc.
Dynamic Properties (msg.ui_update)
msg.ui_update = { format: "<p>New template content</p>" };
EVOLV Key Rules
- Use templates sparingly — prefer built-in widgets when they fit.
- For complex custom visualizations (SVG P&ID, animated schematics), template is the right choice.
- Always use
<style scoped>to avoid leaking CSS to other widgets. - Prefer
watch: { msg }over socket listeners for simpler code. - Keep templates under 100 lines — split complex UIs into multiple template nodes.