NEWS
Skript im VIS-2 Editor funktioniert nur im Editor
-
Hallo liebe Community,
ich möchte mit einem HTML Widget ein interaktives Dropdown Menü erstellen, wo der Nutzer einzelne Messwerte auswählen kann und die Auswahl in einem Datenpunkt gespeichert wird. Anschließend soll er auf "Auswerten" klicken können, dann werden die gewählten Messdaten aus der InfluxDB im gewählten Zeitraum gefischt und in einer CSV zum Download bereitgestellt.
Die verfügbaren Messwerte liegen auch in einem Datenpunkt, die zuvor ein Crawler Skript erstellt hat.
Mein Problem ist jetzt, dass der Javascript Code, der das Dropdown-Menü befüllen soll, nur im Editor der VIS-2 funktioniert im Runtime Modus, aber nicht im Browser (Firefox und Chrome geht beides nicht, sowie die IOBroker App auf iOS auch nicht).
Woran kann das liegen?
HTML-Widget:
<div id="dropdown-container" style="position: relative; display: inline-block; width: 200px;"> <!-- Button für das Dropdown --> <div id="dropdown-toggle" class="btn_grafana_style" style="position: relative; display: flex; align-items: center; justify-content: space-between; padding: 10px; cursor: pointer;"> <span>Messwerte auswählen</span> <!-- Pfeil-Symbol --> <span id="dropdown-arrow" style="margin-left: 10px; font-size: 12px;">▼</span> </div> <!-- Dropdown-Menü --> <div id="dropdown-menu" style="position: absolute; top: 100%; left: 0; width: 100%; background-color: white; border: 1px solid #ccc; padding: 10px; z-index: 1000; max-height: 300px; overflow-y: auto; display: none;"> <!-- Hier werden die Checkboxen dynamisch hinzugefügt --> </div> </div>
Javascript Code im Bereich "Skripte" im VIS Editor:
// IDs der Datenpunkte const sourceDataPoint = "0_userdata.0.Export.Verfügbare_Messwerte"; // JSON mit Messwerten const targetDataPoint = "0_userdata.0.Export.Ausgewählte_Messwerte"; // Array für ausgewählte Messwerte // Dropdown-Elemente const dropdownToggle = document.getElementById("dropdown-toggle"); const dropdownMenu = document.getElementById("dropdown-menu"); // Funktion: Dropdown anzeigen/verstecken dropdownToggle.addEventListener("click", (event) => { event.stopPropagation(); // Verhindert, dass der Klick das Schließen triggert dropdownMenu.style.display = dropdownMenu.style.display === "none" ? "block" : "none"; }); // Funktion: Schließt das Dropdown, wenn man außerhalb klickt document.addEventListener("click", (event) => { if (!dropdownMenu.contains(event.target) && event.target !== dropdownToggle) { dropdownMenu.style.display = "none"; } }); // Funktion: Verfügbare Messwerte laden und Checkboxen generieren function loadMeasurements() { vis.conn.getStates(sourceDataPoint, (err, state) => { if (err || !state[sourceDataPoint] || !state[sourceDataPoint].val) { console.error("Fehler beim Laden der Messwerte:", err || "Keine Daten gefunden"); return; } // JSON-Daten parsen const data = JSON.parse(state[sourceDataPoint].val); // Dropdown leeren dropdownMenu.innerHTML = ""; // Checkboxen für alle Messwerte generieren data.forEach((item) => { const bucket = item.bucket; item.measurements.forEach((measurement) => { const checkboxId = `checkbox-${bucket}-${measurement}`; const checkboxContainer = document.createElement("div"); checkboxContainer.style.marginBottom = "5px"; const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.id = checkboxId; checkbox.dataset.bucket = bucket; checkbox.dataset.measurement = measurement; const label = document.createElement("label"); label.htmlFor = checkboxId; label.textContent = `${bucket}: ${measurement}`; checkboxContainer.appendChild(checkbox); checkboxContainer.appendChild(label); dropdownMenu.appendChild(checkboxContainer); }); }); // Event-Listener für Änderungen an den Checkboxen hinzufügen Array.from(dropdownMenu.querySelectorAll("input[type='checkbox']")).forEach((checkbox) => { checkbox.addEventListener("change", saveSelection); }); }); } // Funktion: Auswahl speichern function saveSelection() { const selectedMeasurements = []; // Alle ausgewählten Checkboxen sammeln Array.from(dropdownMenu.querySelectorAll("input[type='checkbox']:checked")).forEach((checkbox) => { selectedMeasurements.push({ bucket: checkbox.dataset.bucket, measurement: checkbox.dataset.measurement }); }); // Auswahl als JSON-String in den Ziel-Datenpunkt speichern vis.setValue(targetDataPoint, JSON.stringify(selectedMeasurements)); } // Messwerte beim Start laden loadMeasurements(); // Falls sich die verfügbaren Messwerte ändern, neu laden vis.conn.subscribe(sourceDataPoint, (id, state) => { if (id === sourceDataPoint && state) { loadMeasurements(); } });
Screenshot Dropdown Menü:
Wie gesagt im Runtime Modus innerhalb des VIS-Editors funktioniert es einwandfrei, inkl. Speichern der ausgewählten Messwerte.
Ich nutze VIS-2 2.9.64
-
In der Browser Konsole erscheinen auch keine Fehlermeldungen. Anscheinend wird das Skript gar nicht geladen
-
Ich löse einmal auf. All Credits to ChatGPT
Damit sich das Dropdown Menü wie gewünscht auch in der Runtime Umgebung öffent, musste ich den Code in das HTML Widget in den Script Tag aufnehmen.
Zusätzlich bestand dann das Problem, dass alle Checkbox Labels als "$undefined:$undefined" angezeigt wurden.
Das Problem liegt daran, dass mein Browser Firefox anscheinend keine Template Strings im Code unterstützt.
Ich musste die Variablen dann auf einfache String-Verkettung umstellen, dann funktioniert es.
const checkboxId = "checkbox-" + bucket + "-" + measurement; label.textContent = bucket + ": " + measurement;
Hier das vollständige, lauffähige HTML Widget, falls jemand etwas vergleichbares aufbauen möchte:
<div id="dropdown-container"> <!-- Button zum Öffnen des Dropdowns --> <button id="dropdown-toggle" class="btn_grafana_style">Messwerte auswählen ▼</button> <!-- Das Dropdown-Menü, das die Checkboxen enthält --> <div id="dropdown-menu" style="display: none; max-height: 70vh; overflow-y: auto; position: absolute; background-color: white; border: 1px solid #ccc; padding: 10px; margin-top: 5px;"> <!-- Dynamische Inhalte der Checkboxen werden hier eingefügt --> </div> </div> <script> // IDs der Datenpunkte const sourceDataPoint = "0_userdata.0.Export.Verfügbare_Messwerte"; // JSON mit Messwerten const targetDataPoint = "0_userdata.0.Export.Ausgewählte_Messwerte"; // Array für ausgewählte Messwerte // Dropdown-Elemente const dropdownToggle = document.getElementById("dropdown-toggle"); const dropdownMenu = document.getElementById("dropdown-menu"); // Funktion: Dropdown anzeigen/verstecken dropdownToggle.addEventListener("click", (event) => { event.stopPropagation(); // Verhindert, dass der Klick das Schließen triggert dropdownMenu.style.display = dropdownMenu.style.display === "none" ? "block" : "none"; }); // Funktion: Schließt das Dropdown, wenn man außerhalb klickt document.addEventListener("click", (event) => { if (!dropdownMenu.contains(event.target) && event.target !== dropdownToggle) { dropdownMenu.style.display = "none"; } }); // Funktion: Verfügbare Messwerte laden und Checkboxen generieren function loadMeasurements() { vis.conn.getStates(sourceDataPoint, (err, state) => { if (err || !state[sourceDataPoint] || !state[sourceDataPoint].val) { console.error("Fehler beim Laden der Messwerte:", err || "Keine Daten gefunden"); return; } // JSON-Daten parsen const data = JSON.parse(state[sourceDataPoint].val); // Dropdown leeren dropdownMenu.innerHTML = ""; // Checkboxen für alle Messwerte generieren data.forEach((item) => { const bucket = item.bucket; item.measurements.forEach((measurement) => { const checkboxId = "checkbox-" + bucket + "-" + measurement; const checkboxContainer = document.createElement("div"); checkboxContainer.style.marginBottom = "5px"; const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.id = checkboxId; checkbox.dataset.bucket = bucket; checkbox.dataset.measurement = measurement; const label = document.createElement("label"); label.htmlFor = checkboxId; label.textContent = bucket + ": " + measurement; checkboxContainer.appendChild(checkbox); checkboxContainer.appendChild(label); dropdownMenu.appendChild(checkboxContainer); }); }); // Event-Listener für Änderungen an den Checkboxen hinzufügen Array.from(dropdownMenu.querySelectorAll("input[type='checkbox']")).forEach((checkbox) => { checkbox.addEventListener("change", saveSelection); }); }); } // Funktion: Auswahl speichern function saveSelection() { const selectedMeasurements = []; // Alle ausgewählten Checkboxen sammeln Array.from(dropdownMenu.querySelectorAll("input[type='checkbox']:checked")).forEach((checkbox) => { selectedMeasurements.push({ bucket: checkbox.dataset.bucket, measurement: checkbox.dataset.measurement }); }); // Auswahl als JSON-String in den Ziel-Datenpunkt speichern vis.setValue(targetDataPoint, JSON.stringify(selectedMeasurements)); } // Messwerte beim Start laden loadMeasurements(); // Falls sich die verfügbaren Messwerte ändern, neu laden vis.conn.subscribe(sourceDataPoint, (id, state) => { if (id === sourceDataPoint && state) { loadMeasurements(); } }); </script>
-
@vippis
Bei vis2 bin ich mir zwar etwas unsicher, aber bei vis1 wird der Skript Reiter sehr früh geladen und ausgeführt. Zu diesem Zeitpunkt existieren in der Regel die Widgets noch nicht.
Daher greifen die Anlage der Event
Listener ins LeereEine weitere Alternative, neben dem, was du jetzt gemacht hast, wäre noch, alle Funktionen in den Skript, Reiter und in den HTML Tag über die Event Attribute, wie beispielsweise onClick
https://www.w3schools.com/jsref/event_onclick.asp
dann die Funktion zu definieren.