Peha01: Ausgeben einer Javascript-Variablen in HTML <body> </body>

Hallo Leute, ich traue mich fast nicht diese Frage zu stellen😱 Zum Problem: Ich habe eine HTML-Seite, in der ich Eingabe-Felder habe die mit POST an den FLASK-Server übergeben werden. Dort werden mit den Eingaben Daten in einer MariaDB gesucht und wieder zurückgegeben. Weier unten findet Ihr den HTML-Code. Zuerst aber zum Aufbau und Ablauf der Seite und was funktioniert:

  1. Von Zeile 13 - 49 ermittle ich per JS zwei Datumswerte aus dem aktuellen Datum und übergebe diese per document.getElementById.... an die Eingabefelder im darauffolgenden <form> - >/form> Aus dem Form werde n die Daten per submit übergeben. Funktioniert!

  2. Zurück vom Server erhalte ich 4 Listen.

    1. Liste energie_date - Ist eine mehrdimensionale Liste, welche dann 105 - 134 an google chart übergeben wird. Dort wird dann eine entsprechende Grafik ausgegeben. Funktioniert!

    - Das folgende bekomme ich NICHT hin !?!? 2. 3 weitere Listen, aus denen ich VOR/oberhalb der Google Grafik einzelne Werte ausgeben möchte. vertraege, zaehler, summen

    Von 73 - 98 lese ich den Listeninhalt von vertraege und weise die Inhalte sprechender Variablen zu Wenn ich JS-Debugger (F12) mir die Inhalte der Variablen anschaue, sind diese richtig gefüllt! Von 94 - 101 versuche ich nun beispielhaft den Wert der Variablen 'lieferant' VOR der Grafik ab <div id='linechart_material'></div> auszugeben.

    Mir steht wohl gewaltig einer auf der Leitung!!!!!

Kann mir jemand auf die Sprünge helfen. HTML und JS sind nun ganz und gar nicht meine Stärken😪

Anbei nun mein HTML-Code:

<!DOCTYPE html>
<html lang="de">
 <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description" content="Energieverbrauch mit Google Charts darstellen">
  <title>Google Chart parametriert durch flask</title>
  <style>
   div linechart_material { height: 20em; }
   form                   { margin-bottom: 2em; } 
  </style>
  <script src="https://www.gstatic.com/charts/loader.js"></script>
  <script type="module">
/*  
  Funktion um Tage von einem Datum abzuziehen oder zu addieren
  days kann deshalb positiv oder negativ sein
*/
          const days2add = -1;
          const eineNull = "0";
          let monat = "";
          let heute = new Date();
          let jahr = heute.getFullYear();
          let monat_1 = (heute.getMonth() + 1);

          if (monat_1 < 10) {

            monat = eineNull + monat_1;
           
           } 
          else {
            monat = monat_1;
           }
                     
          let tag = "";  
          let tag_l = (heute.getDate() + days2add);
          
          if (tag_l < 10) {
            tag = eineNull + tag_l;
           }
          else {
            tag = tag_l;
           }
      
          let day_new = jahr + "-" + monat + "-" + tag;


          document.getElementById("day_from").value = day_new;
          document.getElementById("day_to").value = day_new;
  </script
 </head>
    <body>
      <h1>Energieverbrauch</h1>
      <form action="{{ url_for('energieshow') }}" method="POST">
       <input type="checkbox" id="energie_art_1" name="energie_art_strom" value="strom", checked="checked">
       <label for="energie_art_1"> Strom</label><br>
       <input type="checkbox" id="energie_art_2" name="energie_art_gas" value="gas">
       <label for="energie_art_2"> Gas</label><br>
       <input type="checkbox" id="energie_art_3" name="energie_art_wasser" value="wasser">
       <label for="energie_art_3"> Wasser</label><br><br>
       
       
       <label for="date">Von :</label>
       <input type="date" id="day_from" name="day_from"  />
       <input type="time" id="time_from" name="time_from" value="00:00" /><br>
       <label for="date">Bis :</label>
       <input type="date" id="day_to" name="day_to" />
       <input type="time" id="time_to" name="time_to" value="23:59" /><br>
       
       <input type="submit" value="Anzeigen"/>
      </form>


<div id='daten_zur_grafik'></div>
    <script>
   
      let zaehlerstand = 123456.0
      let netto_preis  = 123.456
      let brutto_preis = 123.456
       
      
      {% for vertrag in vertraege %}
          von_datum_vertrag = {{vertrag[0]}}
          bis_datum_vertrag = {{vertrag[1]}} 
          energie_art       = {{vertrag[2]}}
          mess_art          = {{vertrag[3]}}
          lieferant         = {{vertrag[4]}}
          vertrags_nr       = {{vertrag[5]}}
          netto_preis       = {{vertrag[6]}}
          brutto_preis      = {{vertrag[7]}}
          preis_einh        = {{vertrag[8]}}
          mass_einh         = {{vertrag[9]}}
      {% endfor %}
      
      document.write(lieferant); 
      
      document.getElementById('lieferant').innerHTML = lieferant;
       
    </script>

<h2>Vertragsdaten zum ausgewählten Zeitraum</h2>
<p> Lieferant <span id="lieferant"></span>.</p>


<div id='linechart_material'></div>
    <script>
      google.charts.load('current', {'packages':['line']});
      google.charts.setOnLoadCallback(drawChart);

    function drawChart() {

      let data = new google.visualization.DataTable() ;
      data.addColumn('string', 'Zeitraum') ;
      data.addColumn('number', 'Verbrauch "{{energie_type}}"') ;

       data.addRows([
          {% for hh_v_b, value, dd_von, dd_bis in energie_data -%}
            ["{{hh_v_b}}", {{ value }}]{%- if not loop.last -%},{% endif %}
          {% endfor %}
      ]);
       
      var options = {
        'width': 1400,
        'height': 500,
        'title': 'Energieverbrauch vom  {{day_from_str}} {{time_from_str}} bis {{day_to_str}} {{time_to_str}}',
        'subtitle': 'in Umdrehungen Ferraris-Scheibe je Zeitspanne'
        };

      var chart = new google.charts.Line(document.getElementById('linechart_material'));

      chart.draw(data, google.charts.Line.convertOptions(options));
    } 
   
    </script>
    </body>
</html>

😪

  1. @@Peha01

    Von 73 - 98 lese ich den Listeninhalt von vertraege und weise die Inhalte sprechender Variablen zu

    Ich will jetzt die Zeilen nicht nachzählen. Du meinst vermutlich diese?

          ___TWIG0___
              von_datum_vertrag = ___TWIG1___
              bis_datum_vertrag = ___TWIG2___ 
              energie_art       = ___TWIG3___
              mess_art          = ___TWIG4___
              lieferant         = ___TWIG5___
              vertrags_nr       = ___TWIG6___
              netto_preis       = ___TWIG7___
              brutto_preis      = ___TWIG8___
              preis_einh        = ___TWIG9___
              mass_einh         = ___TWIG10___
          ___TWIG11___
    

    Schau dir mal den von Twig(?) generierten HTML-Quelltext an! Ich kann mir nicht vorstellen, dass es das ist, was du willst.

    Und globale JavaScript-Variablen sind meh.

    Von 94 - 101 versuche ich nun beispielhaft den Wert der Variablen 'lieferant' VOR der Grafik ab <div id='linechart_material'></div> auszugeben.

          document.write(lieferant); 
    

    Aus welchem Museum hast du document.write() entwendet? Bring das bitte wieder dorthin zurück!

          document.getElementById('lieferant').innerHTML = lieferant;
           
        </script>
    
    <h2>Vertragsdaten zum ausgewählten Zeitraum</h2>
    <p> Lieferant <span id="lieferant"></span>.</p>
    

    Der Quelltext wird im Browser von oben nach unten ausgeführt. Das heißt: zum Zeitpunkt, wo das JavaScript ausgeführt wird, gibt es im DOM noch gar kein Element mit der ID "lieferant". In nichts kann auch nichts reingeschrieben werden.

    Es gibt gute Gründe, JavaScript erst nach dem Rendern der Seite auszuführen; also JavaScript am Ende des body zu notieren (oder anders dafür zu sorgen).

    🖖 Живіть довго і процвітайте

    --
    „Ukončete, prosím, výstup a nástup, dveře se zavírají.“
  2. @@Peha01

    HTML und JS sind nun ganz und gar nicht meine Stärken😪

    Dafür machst du bei der Beschriftund der Formularfelder schon vieles richtig. Aber nicht alles:

           <label for="date">Von :</label>
           <input type="date" id="day_from" name="day_from"  />
           <input type="time" id="time_from" name="time_from" value="00:00" /><br>
           <label for="date">Bis :</label>
           <input type="date" id="day_to" name="day_to" />
           <input type="time" id="time_to" name="time_to" value="23:59" /><br>       
    

    Es gibt kein Element mit der ID "date". Die Felder für Datum und die Felder für Uhrzeit haben keine Beschriftung.

    Möglich wäre sowas:

           <label id="label_date" hidden>Datum</label>
           <label id="label_time" hidden>Uhrzeit</label>
           <label id="label_from">Von:</label>
           <input type="date" name="day_from" aria-labelledby="label_from label_date" />
           <input type="time" name="time_from" value="00:00" aria-labelledby="label_from label_time" /><br>
           <span id="label_from">Bis:</span>
           <input type="date" name="day_to" aria-labelledby="label_to label_date" />
           <input type="time" id="time_to" name="time_to" value="23:59" aria-labelledby="label_to label_time" /><br>
    

    Die label-Elemente ohne for-Attribut könnten jetzt auch spans sein.

    Auch möglich:

         <fieldset>Von:</fieldset>
           <label for="date_from" class="visually-hidden">Datum</label>
           <input type="date" id="day_from" name="day_from" />
           <label for="time_from" class="visually-hidden">Uhrzeit</label>
           <input type="time" id="time_from" name="time_from" value="00:00" />
         </fieldset>
         <fieldset>Bis:</fieldset>
           <label for="date_to" class="visually-hidden">Datum</label>
           <input type="date" id="day_to" name="day_to" />
           <label for="time_to" class="visually-hidden">Uhrzeit</label>
           <input type="time" id="time_to" name="time_to" value="23:59" />
         </fieldset>
    

    mit den nötigen Angaben für visuelle Versecken. Und für fieldset den Rahmen mit CSS entfernen.


    Willst du nun polyglotte Syntax (<input />) verwenden oder nicht (<br>)? Mal, mal so mach wenig Sinn.

    <br> macht sowieso wenig Sinn. Zu Anordnen der Elemente ist CSS da. Sinnvoll dürfte sowas sein:

    label {
      display: block;
      width: fit-content;
    }
    

           <input type="submit" value="Anzeigen"/>
    

    Für Buttons solltest du aus Gründen nicht input, sondern button verwenden:

           <button type="submit">Anzeigen</button>
    

    Wobei type="submit" der Default ist, also nicht angegeben werden muss.

    🖖 Живіть довго і процвітайте

    --
    „Ukončete, prosím, výstup a nástup, dveře se zavírají.“
  3. @@Peha01

              if (monat_1 < 10) {
    
                monat = eineNull + monat_1;
               
               } 
              else {
                monat = monat_1;
               }
    

    Man kann’s auch umständlich machen. Oder man nutzt die in JavaScript für sowas vorgesehene Methode:

      monat = monat_1.padStart(2, '0');
    

    Für den Tag entsprechend.

    Die Variablen finde ich nicht wirklich gut benannt.

    🖖 Живіть довго і процвітайте

    --
    „Ukončete, prosím, výstup a nástup, dveře se zavírají.“
    1. Hallo Gunnar Bittersmann,

      monat = monat_1.padStart(2, '0');

      besser noch wäre, wenn man

      const day_new = (new Date()).toISOString().substring(0,10);
      

      verwendet. Die Klammern um new Date() sind nicht nötig, aber ich find's dann verständlicher. toISOString() ist kein Urgestein, aber hinreichend unterstützt.

      Andere verwenden
      new Intl.DateTimeFormat("sv-SE", { dateStyle: "short" })
      aber das find ich (a) umständlicher und (b) zerbrechlich. Was, wenn die Schweden ihr kurzes Datumsformat nicht mehr als yyyy-mm-dd festlegen?

      Rolf

      --
      sumpsi - posui - obstruxi
      1. @@Rolf B

        besser noch wäre, wenn man

        const day_new = (new Date()).toISOString().substring(0,10);
        

        verwendet.

        Das ist aber nicht Y10k-sicher.

        🖖 Живіть довго і процвітайте

        --
        „Ukončete, prosím, výstup a nástup, dveře se zavírají.“
        1. Hallo Gunnar,

          bis dahin verwenden wir eh Stardates.

          Rolf

          --
          sumpsi - posui - obstruxi
        2. @@Gunnar Bittersmann

          const day_new = (new Date()).toISOString().substring(0,10);
          

          Das ist aber nicht Y10k-sicher.

          const day_new = (new Date()).toISOString().split('T', 1)[0];
          

          ist es. Und auch BC-sicher.

          🖖 Живіть довго і процвітайте

          --
          „Ukončete, prosím, výstup a nástup, dveře se zavírají.“
          1. Hallo Gunnar,

            das mit dem Stardate war kein Scherz.

            In the year 9595
            I'm kinda wondering if man is gonna be alive
            He's taken everything this old earth can give
            And he ain't put back nothing

            Und deshalb sind Y10K Probleme irrelevant. Päpste gibt's bis dahin vermutlich nicht mehr, aber trotzdem weißt Du nicht, wieviele Kalenderreformen es bis dahin gegeben haben wird und ob das T nicht vielleicht durch ein  (\uF8FE)[1] ersetzt wurde.

            Rolf

            --
            sumpsi - posui - obstruxi

            1. Gemäß KLI der Codepoint für p - für - poH - lt Google Translate die Übersetzung für Zeit. ↩︎

  4. Hallo Peha01,

    nur um einem möglichen Verständnisproblem vorzubeugen: weißt Du eigentlich, was <script type="module"> bedeutet?

    Damit legst Du ein ECMAScript Modul fest, mit den folgenden Eigenschaften:

    • Es ist das Las Vegas der Variablen (was Du im Modul an Variablen deklarierst, bleibt im Modul)
    • Modul-Skripte haben automatisch die defer-Option, d.h. sie werden erstmal auf Halde gelegt und dann ausgeführt, wenn das HTML komplett eingelesen ist und das DOM aufgebaut wurde. Und DANACH folgt der DOMContentLoaded Eventhandler.

    Deswegen funktioniert überhaupt deine Initialisierung der Datum-Eingabefelder, obwohl das Script im head steht.

    Was mir bei Deiner Verträge-Schleife auffällt: Woher kommt diese Variable überhaupt? Gunnar vermutete Twig als HTML Generator, aber ist das so? Woher kommt überhaupt die vertraege Variable an dieser Stelle, dafür sehe ich keine Zuweisung.

    Ein kurzer Blick in die Flask-Doku sagt mir, dass diese {% %} Marker zu Jinja2 Templates gehören dürften. Und das heißt: Du hast das vertraege-Array gar nicht in JavaScript, sondern nur am Server. Und dann ist nicht zielführend, serverseitig eine Schleife darüber laufen zu lassen und pro Vertrag einen Zuweisungsblock in JavaScript zu generieren. Die so erzeugten Variablen überschreiben sich pro Durchlauf eh gegenseitig. Wenn Du 4 Verträge hättest und nur den Lieferanten zuweisen würdest, dann würde aus

          {% for vertrag in vertraege %}
              lieferant         = {{vertrag[4]}}
          {% endfor %}
    

    in der Webseite dies:

              lieferant         = "liefer1"
              lieferant         = "liefer2"
              lieferant         = "liefer3"
              lieferant         = "liefer4"
    

    D.h. der letzte gewinnt. Ist das die richtige Logik?

    Bei einer Mischung von Server- und Client-Programmierung musst Du genau aufpassen, was Serverprogramm und was Clientprogramm ist. Flask ist ein Python-Framework, d.h. Du hast sicherlich auch noch eigenen Python-Code laufen. Die Abfolge sollte - wenn Flask etwas taugt - so sein:

    • Dein Python-Programm läuft und ermittelt energie_data, vertraege und so weiter. Zum Schluss kümmert sich entweder Flask darum, ein Template auszufüllen, oder Du hast das in deinem Python-Code selbst programmiert.
    • Das Jinja-Template wird ausgefüllt und zum Browser geschickt
    • Der Browser lädt die HTML Seite und führt derweil und hinterher JavaScript aus.

    Das, was Du mit {% ... %} im Template stehen hast, ist Jinja-Templatesprache und wird im Schritt 2 ausgeführt. Der Browser hat in diesem Moment noch kein Byte an Daten empfangen. Was Du mit {{ }} im Template stehen hast, wird im Schritt 2 ins Template eingesetzt.

    Erst, wenn das Template fertig ausgefüllt ist, wird das Ergebnis zum Browser geschickt. (Okay - vielleicht sendet Jinja die Zeilen, die es fertig hat, sofort an den Browser, um die Zeitdauer zu verkürzen. Aber vom Programmiermodell her ist das gleichbedeutend).

    Steht in allen Verträgen der gleiche Lieferant drin, so dass Du nach Lust und Laune einen verwenden kannst? In dem Fall würde ich das Problem am Server und im Template lösen. Den Lieferanten aus dem ersten Vertrag bekommst Du vermutlich so (ich kann kein Python, ich rate nur) und hast damit in JavaScript gar nichts zu tun:

    <p> Lieferant <span id="lieferant">{{vertraege[0][4]}}</span>.</p> 
    

    Gewöhne Dir bitte auch an, jedes JavaScript-Statement mit einem Semikolon abzuschließen. JavaScript ist da sehr gnädig, aber wehe, Du lässt das Semikolon weg und er findet in der Folgezeile etwas, mit dem er syntaktisch korrekt fortsetzen kann. Dann baut er es gnadenlos zusammen. Ich habe neulich dazu hier etwas aufgeschrieben.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf, erst mal ganz vielen Dank für Deine Tipps und Hinweise. Nun versuche ich mal Deine Frage so gut ich kann zu beantworten.

      1. Zu <script type="module">, das war auch ein Tipp, glaube sogar von Gunnar, weil ich eben meine Input-Felder vorbelegen wollte. Ich denke dafür ist mein Vorgehen ok oder gibt es noch eine andere/bessere Lösung?

      2. Woher kommen die Variablen? Ich hatte ja erwähnt, dass ich auf Serverseite FLASK verwende, das ich vielleicht zu 80% verstanden habe. Also mit POST und dem submit werden meine Inputs an eine Python-App, die auf dem FLASK-Server läuft übertragen. Dann hole ich mir per Python-Programmierung die Daten aus meiner DB und übergebe diese Daten (energie_daten, vertraege, u.s.w ), es sind in PYTHON Listen, per FLASK-Befehl "render_template" an das Template. Was bisher funktioniert, und NICHT von mir kreiert wurde sondern durch Unterstützung, ist der Aufbau der Datenstruktur für google.charts und dann die Ausgabe der Grafik in dem <div id='linechart_material'></div>

      Nun wollte ich eben noch Text, wie Vertragsdaten und Summenwerte, für den betrachteten Zeitraum oberhalb der Grafik augeben. Diese sind in den Listen, vertraege, summen, ... enthalten. Mein nächster Ansatz ist nun, dass ich mit der Ereignisverarbeitung 'document.addEventListener('DOMContentLoaded', function ()' auf das Drücken des Submit-Buttons reagiere und dann meine Werte ausgebe. Die Liste vertraege kann mehrzeilig sein und ich muss prüfen, wieviel Zeilen sie enthält, damit ich mir nicht Werte überschreibe. Das muss ich dann wohl mit jnia2 im Template realisieren?! Naja, dachte es geht einfacher😏

      Eigentlich suchte ich nach einer einfachen Lösung, Energieverbrauchsdaten aus der DB grafisch im Browser anzuzeigen und FLASK schien mir eine günstige Lösung. Muss ich mich doch noch mehr in FLASK einlesen.

      Trotzdem vielen Dank nochmal für Deine Tipps, habe wenigstens wieder etwas dazugelernt😀

      Peter

      1. Hallo Peha01,

        Nun wollte ich eben noch Text, wie Vertragsdaten und Summenwerte, für den betrachteten Zeitraum oberhalb der Grafik augeben.

        Ja, und da ist JavaScript nicht unbedingt das richtige Werkzeug. Wenn Du die Daten am Server hast, kannst Du sie direkt über das Template einsteuern. Du kannst das im Python tun, bevor Du render_template aufrufst. Wie die Datenbereitstellung zwischen Python und Template funktioniert, weiß ich nicht, das kennst Du besser als ich. Ggf. musst Du die in Python ermittelten Werte noch an das Template übergeben.

        JavaScript nimmst Du nur für die Sachen, die sich am Server nicht erledigen lassen. Der Aufbau des addRows()-Aufrufs in drawChart ist so ein Ding.

        Aber die Vorbelegung der Date-Felder könnte etwas sein, was besser aus Python kommt. Das value-Attribut für die date-Felder kannst Du sicherlich auch mittels {{heute}} aus einer Python-Variable namens heute bestücken.

        Für jede Aufgabe das richtige Werkzeug zu finden ist natürlich nicht immer einfach, dazu muss man seinen Werkzeugkasten kennen und wissen, dass man mit einem Hammer zwar eine 4mm-Spaxschraube in ein M4-Loch hineinbekommen kann, es aber bessere Möglichkeiten und Kombinationen gibt.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hallo Rolf,

          nochmal ganz lieben Dank für Deine Unterstützung und Aufklärung in Bezug auf welches Werkzeug für welche Anforderung bei Webseiten.

          Inzwischen habe ich meine Daten mit Jinia/Flask im Template und es funzt👍

          Peter