Auffang-Element in app.js: ein fehlendes Element bricht nicht mehr den Render
Gemessen: 95 direkte $("id")-Zugriffe, 0 davon abgesichert - obwohl die Regel
"neue Elemente per const el = $(..); if (el) {..}" seit 01.08. in CLAUDE.md
steht. Eine Regel mit 0 Prozent Befolgung ist keine Regel, also loest es jetzt
der Code an EINER Stelle statt an 95.
$() gibt bei fehlendem Element ein detachiertes <span> zurueck statt null,
plus einmalige console.warn je ID (kein stiller Fallback - das war der
catch{}-Fehler in pollSnapshot).
?. war NICHT die Loesung: 80 der 95 Zugriffe sind Zuweisungen, und
$("x")?.y = v ist ein Syntaxfehler.
$streng() ist der wichtige Teil. Alle Eingabefelder sind type="number", und
leer heisst dort ausdruecklich "Stop entfernen" (sl: null). Mit dem
Auffang-Element waere .value undefined -> parseFloat NaN -> JSON.stringify
macht daraus wieder null: eine fehlende Eingabemaske haette den Broker-SL
einer laufenden Position stillschweigend geloescht. Betroffen war genau eine
Funktion (sendSltp); setSrMin/setEmg/setManualMargin/setMarginPct fangen NaN
bereits ab (geprueft, nicht angenommen).
Beide Richtungen an einem Minimal-DOM verifiziert, das eine alte index.html
simuliert: mit der alten $-Definition bricht der Render beim ZWEITEN Feld ab,
mit der neuen laeuft er durch. Der Order-Pfad bricht in beiden Faellen sauber
ab.
Asset-Version v=149 -> v=150.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e7112e1ab6
commit
3e8fa9fea8
@@ -226,6 +226,40 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
|
|||||||
```
|
```
|
||||||
Gegenprobe nach jedem Kompilieren: `#property version` im Terminal-`.mq5` **und**
|
Gegenprobe nach jedem Kompilieren: `#property version` im Terminal-`.mq5` **und**
|
||||||
der Zeitstempel der `.ex5` müssen zum erwarteten Stand passen.
|
der Zeitstempel der `.ex5` müssen zum erwarteten Stand passen.
|
||||||
|
- **✅⚠ AUFFANG-ELEMENT statt Disziplin (Fix 2026-08-07, v=150) — die Regel
|
||||||
|
direkt darunter wird jetzt vom CODE durchgesetzt.** `$()` gibt bei fehlendem
|
||||||
|
Element nicht mehr `null` zurück, sondern ein **detachiertes `<span>`**:
|
||||||
|
```js
|
||||||
|
const _FEHLT = document.createElement("span");
|
||||||
|
const $ = (id) => document.getElementById(id) || _FEHLT; // + 1× console.warn je ID
|
||||||
|
const $streng = (id) => document.getElementById(id); // gibt null
|
||||||
|
```
|
||||||
|
⚠⚠ **Anlass ist eine Messung, kein Gefühl: die Regel „neue Elemente per
|
||||||
|
`const el = $(…); if (el) {…}`" war bei 0 von 95 direkten Zugriffen befolgt.**
|
||||||
|
95 Stellen, an denen ein fehlendes Element den **GESAMTEN** Render abbricht;
|
||||||
|
0 davon abgesichert. Eine Regel mit 0 % Befolgung ist keine Regel — deshalb
|
||||||
|
löst es der Code an EINER Stelle statt an 95.
|
||||||
|
⚠ **`?.` war NICHT die Lösung** (mein erster Vorschlag, und er war falsch):
|
||||||
|
**80 der 95** Zugriffe sind ZUWEISUNGEN, und `$("x")?.y = v` ist ein
|
||||||
|
**Syntaxfehler** — Optional Chaining ist links vom `=` verboten.
|
||||||
|
⚠⚠ **`$streng` ist der wichtige Teil.** Alle Eingabefelder sind
|
||||||
|
`type="number"`, und **leer heißt dort ausdrücklich „Stop entfernen"**
|
||||||
|
(`sl: null`). Mit dem Auffang-Element wäre `.value` `undefined` → `parseFloat`
|
||||||
|
→ NaN → `JSON.stringify` macht daraus wieder **`null`** — eine fehlende
|
||||||
|
Eingabemaske hätte den **Broker-SL einer laufenden Position stillschweigend
|
||||||
|
gelöscht.** Ein lauter Absturz ist dort das bessere Verhalten. Betroffen war
|
||||||
|
genau **eine** Funktion (`sendSltp`); `setSrMin`/`setEmg`/`setManualMargin`/
|
||||||
|
`setMarginPct` fangen NaN bereits ab (geprüft, nicht angenommen).
|
||||||
|
⚠ Der Fehler wird **nicht verschluckt**, sondern **1× je ID** in die Konsole
|
||||||
|
gemeldet — ein stiller Fallback wäre derselbe Fehler wie der `catch {}` in
|
||||||
|
`pollSnapshot`, der Render-Fehler lautlos gefressen hat (behoben 01.08.).
|
||||||
|
✅ **Beide Richtungen verifiziert** an einem Minimal-DOM, das eine alte
|
||||||
|
`index.html` simuliert: mit der alten `$`-Definition bricht der Render beim
|
||||||
|
**zweiten** Feld ab, mit der neuen läuft er durch und nur die fehlenden Felder
|
||||||
|
bleiben leer; der Order-Pfad bricht in beiden Fällen sauber ab.
|
||||||
|
⚠ Die 40 vorhandenen `if (!el) return;`-Wächter feuern jetzt nicht mehr. Sie
|
||||||
|
bleiben stehen (kein Nutzen im Entfernen, und sie greifen weiter, wenn jemand
|
||||||
|
`$streng` benutzt) — beim Lesen aber mitdenken.
|
||||||
- **⚠ NEUE HTML-Elemente IMMER null-sicher ansprechen (Fix 2026-08-01).**
|
- **⚠ NEUE HTML-Elemente IMMER null-sicher ansprechen (Fix 2026-08-01).**
|
||||||
`index.html` und `app.js` werden vom Browser **unabhängig** gecacht. Trifft neue JS
|
`index.html` und `app.js` werden vom Browser **unabhängig** gecacht. Trifft neue JS
|
||||||
auf eine alte, gecachte HTML, wirft ein direkter Zugriff (`$("neu").textContent = …`)
|
auf eine alte, gecachte HTML, wirft ein direkter Zugriff (`$("neu").textContent = …`)
|
||||||
@@ -4117,7 +4151,7 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
|
|||||||
(„⚠ die Welle (steuert die Order) schweigt").
|
(„⚠ die Welle (steuert die Order) schweigt").
|
||||||
Live gegengerechnet, beide Zeilen erscheinen wie beabsichtigt.
|
Live gegengerechnet, beide Zeilen erscheinen wie beabsichtigt.
|
||||||
⚠ Reine Anzeige — an Gewichten, Gates und Order-Logik ist nichts geändert.
|
⚠ Reine Anzeige — an Gewichten, Gates und Order-Logik ist nichts geändert.
|
||||||
- Asset-Version aktuell **v=149** (in `web/index.html` hochzählen, siehe Workflows).
|
- Asset-Version aktuell **v=150** (in `web/index.html` hochzählen, siehe Workflows).
|
||||||
Schriftgrößen 2026-07-24 global **+4px** (2× je +2px auf User-Wunsch; Body-Basis
|
Schriftgrößen 2026-07-24 global **+4px** (2× je +2px auf User-Wunsch; Body-Basis
|
||||||
14→18px). ⚠ Betrifft in `style.css` sowohl `font-size:Npx` (71×) ALS AUCH die
|
14→18px). ⚠ Betrifft in `style.css` sowohl `font-size:Npx` (71×) ALS AUCH die
|
||||||
`font:<weight> Npx/…`-**Shorthand** (3×: `.ms-chip`/`.ms-bos-lbl`/`.sqm-badge` —
|
`font:<weight> Npx/…`-**Shorthand** (3×: `.ms-chip`/`.ms-bos-lbl`/`.sqm-badge` —
|
||||||
|
|||||||
+51
-3
@@ -1,6 +1,44 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
// ── Live-Anbindung an server.py via WebSocket (Fallback: Polling) ──────────
|
// ── Live-Anbindung an server.py via WebSocket (Fallback: Polling) ──────────
|
||||||
const $ = (id) => document.getElementById(id);
|
|
||||||
|
// ⚠⚠ AUFFANG-ELEMENT (2026-08-07) — behebt die haeufigste Ausfallart des
|
||||||
|
// Dashboards. `index.html` und `app.js` werden vom Browser UNABHAENGIG
|
||||||
|
// gecacht; trifft neue JS auf alte HTML, war `$("neu").textContent = …` eine
|
||||||
|
// TypeError — und weil das mitten in `render()` passiert, brach der GESAMTE
|
||||||
|
// Render ab: das Dashboard fror auf Altwerten ein, obwohl Server und Snapshot
|
||||||
|
// korrekt waren. Real passiert beim HL-Kurs-Einbau (01.08.), beinahe erneut
|
||||||
|
// beim Entfernen der Setup-Zeile (06.08., 8 Folgezugriffe auf `w`).
|
||||||
|
// Die Gegenregel „neue Elemente per `const el = $(…); if (el) {…}`" stand seit
|
||||||
|
// 01.08. in CLAUDE.md — GEMESSEN befolgt wurde sie bei **0 von 95** direkten
|
||||||
|
// Zugriffen. Eine Regel mit 0 % Befolgung ist keine Regel, also loest es jetzt
|
||||||
|
// der Code an EINER Stelle statt an 95.
|
||||||
|
// ⚠ `?.` waere hier keine Loesung gewesen: **80 der 95** Zugriffe sind
|
||||||
|
// ZUWEISUNGEN, und `$("x")?.y = v` ist ein Syntaxfehler (Optional Chaining ist
|
||||||
|
// links vom `=` verboten).
|
||||||
|
// ⚠ Der Fehler wird NICHT verschluckt, sondern EINMAL je ID in die Konsole
|
||||||
|
// gemeldet — ein stiller Fallback waere derselbe Fehler wie der `catch {}` in
|
||||||
|
// `pollSnapshot`, der Render-Fehler lautlos gefressen hat (behoben 01.08.).
|
||||||
|
const _FEHLT = document.createElement("span");
|
||||||
|
const _fehlendGemeldet = new Set();
|
||||||
|
const $ = (id) => {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
if (el) return el;
|
||||||
|
if (!_fehlendGemeldet.has(id)) {
|
||||||
|
_fehlendGemeldet.add(id);
|
||||||
|
console.warn(`[app] Element #${id} fehlt — vermutlich alte index.html im `
|
||||||
|
+ `Cache. Schreibzugriffe laufen ins Leere, der Rest rendert `
|
||||||
|
+ `weiter. Abhilfe: ?v=N in index.html hochzaehlen.`);
|
||||||
|
}
|
||||||
|
return _FEHLT;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ⚠ STRENGE Variante — gibt `null` zurueck, wenn das Element fehlt.
|
||||||
|
// Zwingend fuer Pfade, die einen gelesenen Wert an den BROKER schicken: dort
|
||||||
|
// ist „Feld fehlt" NICHT dasselbe wie „Feld ist leer". Beim SL/TP-Feld heisst
|
||||||
|
// leer ausdruecklich „Stop entfernen" — ein Auffang-Element wuerde daraus ein
|
||||||
|
// stilles Loeschen des Broker-SL einer laufenden Position machen. Ein lauter
|
||||||
|
// Absturz ist dort das bessere Verhalten als ein stiller Schaden.
|
||||||
|
const $streng = (id) => document.getElementById(id);
|
||||||
|
|
||||||
function fmt(v, d = 3) {
|
function fmt(v, d = 3) {
|
||||||
return (v === null || v === undefined || isNaN(v)) ? "—" : Number(v).toFixed(d);
|
return (v === null || v === undefined || isNaN(v)) ? "—" : Number(v).toFixed(d);
|
||||||
@@ -1359,10 +1397,20 @@ document.querySelectorAll(".btn[data-side]").forEach(btn => {
|
|||||||
|
|
||||||
// Manuelles SL/TP (Trade-Bereich) — sendet beide Felder; deaktiviert das Trailing.
|
// Manuelles SL/TP (Trade-Bereich) — sendet beide Felder; deaktiviert das Trailing.
|
||||||
async function sendSltp() {
|
async function sendSltp() {
|
||||||
const slv = $("tb-sl").value, tpv = $("tb-tp").value;
|
// ⚠ STRENG (s. `$streng` oben): hier ist „Feld fehlt" NICHT „Feld ist leer".
|
||||||
|
// Leer heisst ausdruecklich „Stop entfernen" (`sl: null`) — mit dem
|
||||||
|
// Auffang-Element waere `.value` `undefined`, `parseFloat` liefert NaN, und
|
||||||
|
// `JSON.stringify` macht daraus wieder `null`. Eine fehlende Eingabemaske
|
||||||
|
// wuerde so den Broker-SL einer laufenden Position stillschweigend loeschen.
|
||||||
|
const elSl = $streng("tb-sl"), elTp = $streng("tb-tp");
|
||||||
|
if (!elSl || !elTp) {
|
||||||
|
toast("SL/TP-Felder nicht geladen — Seite neu laden (Strg+F5)", false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const slv = elSl.value, tpv = elTp.value;
|
||||||
const tok = await ensureToken();
|
const tok = await ensureToken();
|
||||||
if (tok === null) return;
|
if (tok === null) return;
|
||||||
$("tb-sl").blur(); $("tb-tp").blur();
|
elSl.blur(); elTp.blur();
|
||||||
try {
|
try {
|
||||||
const r = await fetch("api/sltp", { method: "POST",
|
const r = await fetch("api/sltp", { method: "POST",
|
||||||
headers: { "X-Auth-Token": tok, "Content-Type": "application/json" },
|
headers: { "X-Auth-Token": tok, "Content-Type": "application/json" },
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@
|
|||||||
<meta name="theme-color" content="#0d1117">
|
<meta name="theme-color" content="#0d1117">
|
||||||
<title>Oil · MT5</title>
|
<title>Oil · MT5</title>
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
<link rel="stylesheet" href="style.css?v=149">
|
<link rel="stylesheet" href="style.css?v=150">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Modul-Menü (ganz oben) -->
|
<!-- Modul-Menü (ganz oben) -->
|
||||||
@@ -330,6 +330,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="toast" class="toast hidden"></div>
|
<div id="toast" class="toast hidden"></div>
|
||||||
|
|
||||||
<script src="app.js?v=149"></script>
|
<script src="app.js?v=150"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user