Hinzufügen neuer Kern-Widgets
Ein einzelnes Widget besteht aus zwei Hauptteilen:
- Ein Node-RED-Knoten, der in der Palette des Node-RED-Editors erscheint
.vue
und clientseitiger Code, der das Widget in ein Dashboard rendert
Sie können unsere Sammlung von Kern-Widgets hier erkunden.
Wir sind immer offen für Pull-Anfragen und neue Ideen zu Widgets, die dem Kern-Dashboard-Repository hinzugefügt werden können.
Beim Hinzufügen eines neuen Widgets zur Kernsammlung müssen Sie die folgenden Schritte befolgen, um sicherzustellen, dass das Widget im Node-RED-Editor verfügbar ist und korrekt in der Benutzeroberfläche gerendert wird.
Empfohlene Lektüre
In der linken Navigation finden Sie einen Abschnitt "Nützliche Anleitungen". Wir empfehlen, sich diese anzusehen, da sie einen guten Überblick über die Struktur des Dashboard 2.0-Codebasis und einige der zugrunde liegenden architektonischen Prinzipien geben, auf denen es aufgebaut ist.
Insbesondere werden die folgenden empfohlen:
Checkliste
Beim Hinzufügen eines neuen Widgets zu Dashboard 2.0 müssen Sie sicherstellen, dass die folgenden Schritte für dieses neue Widget befolgt wurden, damit es erkannt und in einem Dashboard 2.0-Build aufgenommen wird:
- In
/nodes/
:- Fügen Sie
<widget>.html
hinzu - Fügen Sie
<widget>.js
hinzu - Fügen Sie den Verweis auf den Abschnitt
node-red/nodes
inpackage.json
hinzu
- Fügen Sie
- In
/ui/
:- Fügen Sie
widgets/<widget>/<widget>.vue
hinzu - Fügen Sie das Widget zur
index.js
-Datei in/ui/widgets
hinzu
- Fügen Sie
Beispiel <widget.vue>
<template>
<div @click="onAction">
{{ id }}
</div>
</template>
<script>
import { useDataTracker } from '../data-tracker.js'
import { mapState } from 'vuex'
export default {
name: 'DBUIWidget',
// wir müssen $socket injizieren, damit wir Ereignisse an Node-RED senden können
inject: ['$socket', '$dataTracker'],
props: {
id: String, // die ID des Widgets, wie von Node-RED definiert
props: Object, // die Eigenschaften für dieses Widget, die im Node-RED-Editor definiert sind
state: Object // der Zustand dieses Widgets, z.B. aktiviert, sichtbar
},
computed: {
// unseren Datenspeicher so abbilden, dass wir auf alle Daten zugreifen können, die an dieses Widget gebunden sind
// bei der Eingabe von Node-RED empfangen
...mapState('data', ['messages']), // bietet Zugriff auf `this.messages`, wobei `this.messages[this.id]` die gespeicherte Nachricht für dieses Widget ist
},
created () {
// das Widget mit Standard-Event-Handlern für onInput, onLoad und onDynamicProperties einrichten
this.$dataTracker(this.id)
},
methods: {
onAction () {
// wir können alle Daten senden, die wir benötigen, um Node-RED durch diesen (optionalen) Nachrichtenparameter zu senden
const msg = {
payload: 'hallo welt'
}
// ein Ereignis an Node-RED senden, um es darüber zu informieren, dass wir auf dieses Widget geklickt haben
this.$socket.emit('widget-action', this.id, msg)
}
}
}
</script>
<style scoped>
</style>
Daten-Tracker
Der Daten-Tracker ist ein global verfügbarer Dienst, der hilft, die Standard-Event-Handler für Widgets einzurichten.
Verwendung
Der Daten-Tracker ist global über bestehende Widgets verfügbar und kann mit this.$dataTracker(...)
aufgerufen werden.
Die einfachste Verwendung des Trackers wäre:
...
created () {
this.$dataTracker(this.id)
},
...
Dies richtet die folgenden Ereignisse ein:
on('widget-load')
- Stellt sicher, dass wir alle empfangenenmsg
-Objekte speichern, wenn ein Widget erstmals in das Dashboard geladen wird.on('msg-input')
- Standardverhalten überprüft auf dynamische Eigenschaften (z.B. Sichtbarkeit, deaktivierter Zustand) und speichert auch die eingehendemsg
im Vuex-Store
Benutzerdefinierte Verhaltensweisen
Es bietet auch Flexibilität, um benutzerdefinierte Event-Handler für ein gegebenes Widget zu definieren. Zum Beispiel haben wir in einem ui-chart
-Knoten eine Logik, die das Zusammenführen von Datenpunkten und das Rendern des Diagramms behandelt, wenn eine Nachricht empfangen wird.
Die Eingaben für die Funktion this.$dataTracker(widgetId, onInput, onLoad, onDynamicProperties)
werden wie folgt verwendet:
widgetId
- die eindeutige ID des WidgetsonInput
- eine Funktion, die aufgerufen wird, wenn eine Nachricht von Node-RED über denon(msg-input)
-Socket-Handler empfangen wirdonLoad
- eine Funktion, die aufgerufen wird, wenn das Widget geladen wird und durch daswidget-load
-Ereignis ausgelöst wirdonDynamicProperties
- eine Funktion, die als Teil deson(msg-input)
-Ereignisses aufgerufen wird und vor der Standard-onInput
-Funktion ausgelöst wird. Dies ist ein guter Einstiegspunkt, um gegen alle Eigenschaften zu prüfen, die in dermsg
enthalten sind, um eine dynamische Eigenschaft zu setzen (d.h. Inhalt, der inmsg.ui_update...
gesendet wird).
Dynamische Eigenschaften
Node-RED ermöglicht die Definition der zugrunde liegenden Konfiguration für einen Knoten. Zum Beispiel hätte ein ui-button
Eigenschaften wie label
, color
, icon
usw. Es wird oft gewünscht, dass diese Eigenschaften dynamisch sind, was bedeutet, dass sie zur Laufzeit geändert werden können.
Es ist eine Standardpraxis innerhalb von Dashboard 2.0, diese Eigenschaftsaktualisierungen über ein verschachteltes msg.ui_update
-Objekt zu unterstützen. Daher können Benutzer erwarten, dass sie diese im Allgemeinen steuern können, indem sie msg.ui_update.<property-name>
an den Knoten übergeben, der wiederum die entsprechende Eigenschaft aktualisieren sollte.
Entwurfsmuster
Dieser Abschnitt skizziert das architektonische Entwurfsmuster für die Entwicklung dynamischer Eigenschaften in ein Widget.
Serverseitig werden dynamische Eigenschaften in unserem state
-Store gespeichert, der eine Zuordnung der Widget-ID zu den dynamischen Eigenschaften darstellt, die diesem Widget zugewiesen sind. Dies geschieht, um sicherzustellen, dass die dynamischen Eigenschaften eines Widgets von der anfänglichen Konfiguration getrennt sind, die in Node-RED definiert und gespeichert ist.
Bevor der ui-base
-Knoten das ui-config
-Ereignis und die Nutzlast sendet, führen wir die dynamischen Eigenschaften mit der anfänglichen Konfiguration zusammen, wobei die dynamischen Eigenschaften die zugrunde liegende Konfiguration überschreiben dürfen. Daher wird der Client, wenn er eine ui-config
-Nachricht erhält, die aktuellste Konfiguration für das Widget haben, mit der Zusammenführung von sowohl statischen als auch dynamischen Eigenschaften.
Dynamische Eigenschaften setzen
Serverseitig
Um eine dynamische Eigenschaft im serverseitigen state
-Store zu setzen, können wir das beforeSend
-Ereignis auf dem Knoten nutzen. Dieses Ereignis wird bei jeder Gelegenheit ausgelöst, bei der der serverseitige Knoten eine Nachricht an den Client senden soll, einschließlich wenn eine neue Eingabe in einen gegebenen Knoten empfangen wird.
Dafür nutzen wir die set
-Funktion des State-Stores:
/**
*
* @param {*} base - zugehöriger ui-base-Knoten
* @param {*} node - das Node-RED-Knotenobjekt, für das wir den Zustand speichern
* @param {*} msg - die vollständig empfangene Nachricht (ermöglicht uns, auf Anmeldeinformationen/Socketid-Beschränkungen zu prüfen)
* @param {*} prop - die Eigenschaft, die wir auf dem Knoten setzen
* @param {*} value - der Wert, den wir setzen
*/
set (base, node, msg, prop, value) {
if (canSaveInStore(base, node, msg)) {
if (!state[node.id]) {
state[node.id] = {}
}
state[node.id][prop] = value
}
},
Zum Beispiel in ui-dropdown
:
const evts = {
onChange: true,
beforeSend: function (msg) {
if (msg.ui_update) {
const update = msg.ui_update
if (typeof update.options !== 'undefined') {
// dynamisch die Eigenschaft "options" setzen
statestore.set(group.getBase(), node, msg, 'options', update.options)
}
}
return msg
}
}
// die Dashboard-UI darüber informieren, dass wir diesen Knoten hinzufügen
group.register(node, config, evts)
Clientseitig
Jetzt, da wir den serverseitigen Zustand aktualisieren, wird bei jedem Aktualisieren die vollständige ui-config
bereits die dynamischen Eigenschaften enthalten.
Wir müssen dann sicherstellen, dass der Client über diese dynamischen Eigenschaften informiert wird, während sie sich ändern. Dazu können wir das onDynamicProperties
-Ereignis verwenden, das im Daten-Tracker verfügbar ist.
Ein gutes Muster, dem man folgen kann, ist, eine computed
-Variable auf der betreffenden Komponente bereitzustellen. Wir bieten dann drei hilfreiche, globale Funktionen:
setDynamicProperties(config)
: Weist die bereitgestellten Eigenschaften (inconfig
) dem Widget im clientseitigen Store zu. Dies aktualisiert automatisch den Zustand des Widgets und alle Verweise, die diese Eigenschaft verwenden.updateDynamicProperty(property, value)
: Aktualisiert die relevanteproperty
mit dem bereitgestelltenvalue
im clientseitigen Store. Stellt auch sicher, dass die Eigenschaft nicht vom Typundefined
ist. Dies aktualisiert automatisch den Zustand des Widgets und alle Verweise, die diese Eigenschaft verwenden.getProperty(property)
: Ruft automatisch den korrekten Wert für die angeforderte Eigenschaft ab. Sucht zuerst in den dynamischen Eigenschaften, und wenn nicht gefunden, wird auf die statische Konfiguration zurückgegriffen, die imui-config
-Ereignis definiert ist.
Die berechneten Variablen können die Funktion this.getProperty
umschließen, die immer mit dem zentralisierten Vuex-Store auf dem neuesten Stand sein wird.
{
// ...
computed: {
label () {
return this.getProperty('label')
}
},
created () {
// wir können einen benutzerdefinierten onDynamicProperty-Handler für dieses Widget definieren
useDataTracker(this.id, null, null, this.onDynamicProperty)
// ...,
methods () {
// ...,
onDynamicProperty (msg) {
// Standardpraxis, um Updates über msg.ui_update zu akzeptieren
const updates = msg.ui_update
// global verfügbare API verwenden, um die dynamische Eigenschaft zu aktualisieren
this.updateDynamicProperty('label', updates.label)
}
}
}
Dokumentation aktualisieren
Es gibt zwei wichtige Orte, um sicherzustellen, dass die Dokumentation aktualisiert wird, wenn dynamische Eigenschaften hinzugefügt werden:
Online-Dokumentation:
Jeder Knoten hat eine entsprechende /docs/nodes/widgets/<node>.md
-Datei, die die Definition einer dynamic`-Tabelle im Frontmatter ermöglicht, z.B.:
dynamic:
Options:
payload: msg.options
structure: ["Array<String>", "Array<{value: String}>", "Array<{value: String, label: String}>"]
Class:
payload: msg.class
structure: ["String"]
Sie können diese Tabelle dann in die Dokumentation rendern mit:
## Dynamische Eigenschaften
<DynamicPropsTable/>
Editor-Dokumentation:
Jeder Knoten hat eine entsprechende /locales/<locale>/<node>.html
-Datei, die eine Tabelle dynamischer Eigenschaften enthalten sollte, z.B.:
<h3>Dynamische Eigenschaften (Eingaben)</h3>
<p>Jede der folgenden Eigenschaften kann an eine <code>msg.</code> angehängt werden, um Eigenschaften auf diesem Knoten zur Laufzeit zu überschreiben oder zu setzen.</p>
<dl class="message-properties">
<dt class="optional">options <span class="property-type">array</span></dt>
<dd>
Ändern Sie die zur Laufzeit verfügbaren Optionen im Dropdown
<ul>
<li><code>Array<string></code></li>
<li><code>Array<{value: String}></code></li>
<li><code>Array<{value: String, label: String}></code></li>
</ul>
</dd>
<dt class="optional">class <span class="property-type">string</span></dt>
<dd>Fügen Sie zur Laufzeit eine CSS-Klasse oder mehr zum Button hinzu.</dd>
</dl>
Debugging dynamischer Eigenschaften
Dashboard 2.0 verfügt über eine Debug-Ansicht, die ein Spezialpanel enthält, um alle dynamischen Eigenschaften zu überwachen, die einem Widget zugewiesen sind. Dies kann ein sehr nützliches Werkzeug sein, um zu überprüfen, ob der Client über alle dynamischen Eigenschaften informiert ist, die gesendet wurden.