Forum: PC-Programmierung Morris Graph mit Ajax


von Kolja L. (kolja82)


Angehängte Dateien:

Lesenswert?

Guten Abend


In der Hoffnung, dass hier jemand sich etwas mit JavaScript  und Ajax 
auskennt,
traue ich mich mal, hier meinen ersten Versuch mit diesen Sprachen zu 
posten:

1
</head>
2
  <script>
3
    function getdata(){
4
    $.getJSON("data1.php?n=1500", function(data) {
5
6
      new Morris.Line({
7
        element:    "graph",
8
        data:        data,
9
        xkey:       "date",
10
        ykeys:      ['wama_1', 'wama_2'],
11
        labels:     ['Wama_1', 'Wama_2'],
12
        lineColors: ['#1111ff',' #ff1111'],
13
        pointSize:  ['0'],
14
        hideHover:  ['always'],
15
        smooth:     ['false']
16
      }); //Morris.Line
17
18
      setTimeout("getdata()",5000);
19
    }); // getJSON
20
21
  } //function getdata
22
23
  getdata()
24
25
  </script>
26
27
<body>
28
    <div id="graph" style="height: 300px"></div>
29
</body>

"Mein" Code besteht eigentlich nur aus dem Line-Graph Beispiel der 
Morris.js Graph Bibliothek.
Prinzipiell funktioniert der Code auch, er werden die Daten aus der 
data1.php Datei gelesen und dargestellt.
Nur wird alle 5 Sekunden ein neuer Graph unter dem bisherigen Graph 
dargestellt / hinzugefügt.
Das soll so natürlich nicht sein.

Wie kann ich erreichen, dass die Daten neu eingelesen werden und der 
Graph damit aktualisiert wird?

Muss der alte Graph erst gelöscht werden?
Ist dann kurzzeitig eine weiße Fläche zu sehen?
Dann wäre das Prinzip ja irgendwie blöd und ich hätte auch alles mit 
einem Iframe und Meta-Refresh lösen können.

Im Anhang ein Bild wie es gerade aussieht.
Das Ganze soll ein Monitoring für unsere beiden Waschmaschinen werden.

Danke und Gruß

Kolja

von Frank L. (Firma: Flk Consulting UG) (flk)


Lesenswert?

Hallo,
Nimm mal die Zeile
[c]
setTimeout("getdata()",5000);
[\c]

aus Deinem Code raus. Damit produzierst Du einen rekursiven Aufruf.
Gruß
Frank

von Kolja L. (kolja82)


Lesenswert?

Hi Frank

Ja, die Zeile ruft die Funktion getdata() alle 5 Sekunden auf,
meine Idee war ja, dass sich der Graph damit alle 5 Sekunden 
aktualisiert.

Wenn ich die Zeile rausnehme, dann ist die Seite ja "statisch".

Daher die Frage: Wie kann ich den div aktualisieren?

Gruß Kolja

von Dunno.. (Gast)


Lesenswert?

Set timeout hinter der Funktionsdeklaration?

Dann wäre es nicht mehr rekursiv (sagt einer der null Ahnung von JS 
hat..)

von Kolja L. (kolja82)


Lesenswert?

Schon versucht, gleiches Ergebnis :-(

von M.K. B. (mkbit)


Lesenswert?

Kolja L. schrieb:
> Wie kann ich erreichen, dass die Daten neu eingelesen werden und der
> Graph damit aktualisiert wird?

Morris Doku:
Note 2: if you need to update the plot, use the setData method on the 
object that Morris.Line returns. There's a setData example in the GitHub 
repo.


Du musst also mit den ersten Daten das Morris.Line Objekt anlegen und in 
einer Variable außerhalb des Scopes von getData anlegen. Bei den 
weiteren Daten rufst du dann auf der Variable setData mit den neuen 
Daten auf.

: Bearbeitet durch User
von Kolja L. (kolja82)


Lesenswert?

Du meinst wahrscheinlich dieses Beispiel:

https://github.com/morrisjs/morris.js/blob/master/examples/updating.html

Das bekomme ich aber nicht mal ohne es zu verändern zum Laufen.
Geschweige, dass ich wüsst, wo ich dort die url zu der php Datei 
einfügen muss.

Ich verstehe dieses Javascript einfch zu wenig.
Vielleicht belasse ich es bei einem meta-refresh im iframe.

von M.K. B. (mkbit)


Lesenswert?

Kolja L. schrieb:
> Du meinst wahrscheinlich dieses Beispiel:

Ich habe mir das Beispiel nicht angeschaut, aber mit meiner Erklärung 
kannst du es ja in deinem Code einfach einbauen. Wenn du nicht 
weiterkommst, dann poste deinen Code mit dem Versuch.

Das Problem ist jetzt nichts JS spezifisches, sondern 
Objektorientierung. Du legst ein Graphobjekt an und dann setzt du immer 
wieder neue Daten.

von sogenannter javascriptprofi (Gast)


Lesenswert?

Du willst dir ausserdem window.setInterval() ansehen.

von Zeno (Gast)


Lesenswert?

Frank L. schrieb:
> Hallo,
> Nimm mal die Zeile
>
1
> setTimeout("getdata()",5000);
2
> [\c]
3
4
Das ist ja auch so gewollt, da er alle 5s aktualisieren möchte. Allerdings ist es ausreichend setTimeout einmal aufzurufen.
5
6
7
Diese Zeile
8
[c]
9
new Morris.Line({ ....
10
[/]
11
12
ist Dein Problem. Du erzeugst jedes Mal einen neuen Graphen.
13
14
Lege Dir beim Aufruf der Seite eine Objectvariable an, z.B. so:
15
[c]
16
   MorrisLine = new Morris.Line();
Schreibe Dir eine Initialisierungsfunktion wo Du diese Variable 
erzeugst. In dieser Funktion kannst Du auch gleich SetTimeout mit unter 
bringen.

In Deiner geposteten Funktion greifst Du einfach auf diese Ojectvariable 
zu und modifizierst die Daten

von Zeno (Gast)


Lesenswert?

Bei den Codeblöcken ist was schief gelaufen - sorry.

von M.K. B. (mkbit)


Lesenswert?

Zeno schrieb:
> Allerdings ist es ausreichend setTimeout einmal aufzurufen.

setTimeout muss immer wieder neu aufgerufen werden. Du meinst vermutlich 
setInterval.

von sid (Gast)


Lesenswert?

ich nehme stark an data liegt als Array vor..
oder ist das ein JSOn Objekt?

Naja, jdf sollte es so funktionieren ohne es getestet zu haben
1
</head>
2
<script>
3
var nReloads = 0;
4
var graph = new Morris.Line({
5
  element:    "graph",
6
  data:       [],
7
  xkey:       "date",
8
  ykeys:      ['wama_1', 'wama_2'],
9
  labels:     ['Wama_1', 'Wama_2'],
10
  lineColors: ['#1111ff',' #ff1111'],
11
  pointSize:  ['0'],
12
  hideHover:  ['always'],
13
  smooth:     ['false']
14
}); //Morris.Line
15
16
function getdata(){
17
  $.getJSON("data1.php?n=1500", function(data)
18
  {
19
    nReloads++;
20
    graph.setData(data);
21
    $('#reloadStatus').text(nReloads + ' reloads');
22
    setTimeout("getdata()",5000);
23
  }); // getJSON
24
25
} //function getdata
26
27
getdata()
28
29
</script>
30
31
<body>
32
<div id="graph" style="height: 300px"></div>
33
<div id="reloadStatus"> initial data</div>
34
</body>

Ich hab mal ein reloadStatus div hinzugefügt gemäss dem Beispielcode,
das sollte die Anzahl der Schleifen zählen, damit Du siehst ob es sich 
aktualisiert auch bei statischen Daten;
kannst es ja nachher entfernen.

Ich halte das setTimeout übrigens für die elegantere Lösung als 
SetInterval,
da es terminiert im Fehlerfall und eben nicht bis in alle Ewigkeit alle 
fünf Sekunden auf dem Server rumhämmert solange die Seite geöffnet ist.

von DPA (Gast)


Lesenswert?

> setTimeout("getdata()",5000);

Ändert das doch bitte auf:
1
setTimeout(getdata,5000);

Es ist nicht schön, wenn der Browser das jedes mal evaln muss.

von TotoMitHarry (Gast)


Lesenswert?

Oder einfach ein:
document.getElementById('graph').innerHTML = "";
als erste Zeile in/hinter  getdata(){ einfügen.

von Frank L. (Firma: Flk Consulting UG) (flk)


Lesenswert?

TotoMitHarry schrieb:
> Oder einfach ein:
> document.getElementById('graph').innerHTML = "";
> als erste Zeile in/hinter  getdata(){ einfügen.

Hallo,

genau so muss es sein. Das Problem hierbei ist, dass die Funktion die 
durch getData mit new Morris() immer ein neues Objekt erzeugt und an das 
div hängt.

Wenn Du vor getData die Kinder des div löschst und anschließend wie 
gehabt getData aufrufst, sollte es funktionieren.

D.h. Dein Ursprungscode war vollkommen korrekt. Lediglich die Zeile von 
TotoMitHarry ergänzen.

Gruß
Frank

von sid (Gast)


Lesenswert?

TotoMitHarry schrieb:
> document.getElementById('graph').innerHTML = "";
> als erste Zeile in/hinter  getdata(){ einfügen.

Oder da jquery im Spiel ist
$('#graph').empty();

AAABER mal ehrlich das Div zu leeren
und neu zu erstellen statt die daten zu aktualisieren

Frank L. schrieb:
> Hallo,
>
> genau so muss es sein. Das Problem hierbei ist, dass die Funktion die
> durch getData mit new Morris() immer ein neues Objekt erzeugt und an das
> div hängt.
>

ist exakt genau FALSCH!
also klar, es funktioniert, ist nur ziemlich das uneleganteste was man 
machen kann...

Das ist wie den Briefkopf für jeden Brief einzeln zu erstellen, statt 
das fertige Template von gestern zu benutzen ;)

Also einmal Briefkopf
(globalen Morris graphen in "graph" abgelegt)
und bei jedem durchlauf nur den Inhalt (Daten) erneuern,
der Webbrowser braucht dann auch weniger CPU ;)

von Frank L. (Firma: Flk Consulting UG) (flk)


Lesenswert?

me culpa, Du hast natürlich recht. Besser ist es den graph zu 
aktualisieren. Aber der TO hat geschrieben keine Ahnung von AJAX und JS. 
So ist es am einfachsten.

Der korrekt Syntax für eine Aktualisierung wäre hier zu finden:

https://github.com/morrisjs/morris.js/blob/master/examples/updating.html

Gruß
Frank

von Zeno (Gast)


Lesenswert?

M.K. B. schrieb:
> Zeno schrieb:
>> Allerdings ist es ausreichend setTimeout einmal aufzurufen.
>
> setTimeout muss immer wieder neu aufgerufen werden. Du meinst vermutlich
> setInterval.

Ja stimmt. Ich verwende auf meine Seite setInterval - habe ich 
verwechselt

von sid (Gast)


Lesenswert?

Frank L. schrieb:
> me culpa, Du hast natürlich recht. Besser ist es den graph zu
> aktualisieren.
kein Ding
Frank L. schrieb:
> Aber der TO hat geschrieben keine Ahnung von AJAX und JS.
genau deswegen war ich kleinlich ;)
Ich find beides tut sich nix was die Komplexität angeht;
und die 'vom Entwickler so vorgesehene' Variante ist in der Regel die 
bessere Wahl ;)

von Kolja L. (kolja82)


Lesenswert?

Moin

Erstmal vielen Dank, dass ihr euch so um mich bemüht.

Ich habe verucht euren Erklärungen zu folgen, aber dafür fehlt mir 
tatsächlich das Wissen um Javascript.

Nun habe ich mir besagtes Beispiel nochmal angeschaut bzw. ausprobiert.
Wenn ich mir das ganze git runterlade und das updating Beispiel starte 
funktioniert es.

Sobald es sich auf meinem Webspace befindet nicht mehr.
Dabei habe ich die vier lokal eingebundenen Skripte einfach mit in das 
Stammverzeichnis gelegt und die Pfade angepasst.
Sind auch alle erreichbar.

Als Ausgabe bekomme ich, neben der Überschrift, einen KAsten mit 
Scrollbalken und dem Inhalt des JS-Skriptes als Text angezeigt.

Kannsich das jemand erklären?

von sid (Gast)


Lesenswert?

Okay, nochmal mit Kommentaren..
vielleicht ist es dann einfacher zu verstehen:
1
</head>
2
<script>
3
//Iniitaliseren zweier globaler Variablen
4
5
var nReloads = 0;
6
// Optional aber für den Test hier ganz nützlich
7
8
9
/* 
10
streng genommen ist "Mgraph" ein Objekt keine Variable, 
11
initialisieren tun wir das dennoch gleich
12
und zwar JETZT damit du nicht jedesmal einen neuen erstellen musst
13
innerhalb der Funktion.
14
*/
15
var Mgraph = new Morris.Line({
16
  element:    "graph",
17
  data:       [],   // initialisiert als leeres Array hier
18
  xkey:       "date",
19
  ykeys:      ['wama_1', 'wama_2'],
20
  labels:     ['Wama_1', 'Wama_2'],
21
  lineColors: ['#1111ff',' #ff1111'],
22
  pointSize:  ['0'],
23
  hideHover:  ['always'],
24
  smooth:     ['false']
25
}); 
26
/*
27
Damit brauchst du nurnoch auf "Mgraph" zu verweisen.
28
Nun zurück zu Deiner Funktion 
29
wir brauchen darin den graphen nichtmehr zu erstellen,
30
das haben wir ja grade eben global getan,
31
deswegen ist sie nun deutlich kürzer!
32
*/
33
function getdata()
34
{
35
  // Ajax Aufruf
36
  $.getJSON("data1.php?n=1500", function(data)
37
  {
38
    // Mgraph ist der oben erstellte globale MorrisGraph, da schieben wir nun die empfangenen Daten rein!
39
    Mgraph.setData(data);
40
    /* fertig!
41
     also falls data1.php ein Array überträgt, wovon ich ausgehe...
42
     ansonsten braucht es einen parser dazwischen, 
43
     da müsste ich erstmal das Datenformat sehen
44
    */
45
46
    // nReload hochzählen für die Anzeige
47
    nReloads++; 
48
    $('#reloadStatus').text(nReloads + ' reloads'); // und angezeigt!
49
  // die beiden Zeilen sind wie gesagt optional  und nur drin um nen sichtbaren Zähler zu haben
50
    
51
  // das TimeOut damit getdata wieder aufgerufen wird in 5 sekunden
52
  setTimeout(getdata,5000);
53
  });
54
55
}
56
57
//und dann wie gehabt der Aufruf:
58
getdata();
59
60
</script>
61
62
<body>
63
<div id="graph" style="height: 300px"></div>
64
<!-- der optionale nReload braucht auch ein Kästchen, das ist natürlich auch optional -->
65
<div id="reloadStatus"> initial data</div>
66
</body>

von sid (Gast)


Lesenswert?

und ohne Zähler und Überschuss wird's
dann auch niedlich klein
1
</head>
2
<script>
3
var graph = new Morris.Line({
4
  element:    "graph",
5
  data:       [],
6
  xkey:       "date",
7
  ykeys:      ['wama_1', 'wama_2'],
8
  labels:     ['Wama_1', 'Wama_2'],
9
  lineColors: ['#1111ff',' #ff1111'],
10
  pointSize:  ['0'],
11
  hideHover:  ['always'],
12
  smooth:     ['false']
13
});
14
function getdata()
15
{
16
  $.getJSON("data1.php?n=1500", function(data)
17
  {
18
    graph.setData(data);
19
    setTimeout(getdata,5000);
20
  });
21
}
22
getdata();
23
</script>
24
<body>
25
<div id="graph" style="height: 300px"></div>
26
</body>

von Kolja L. (kolja82)


Lesenswert?

Hi sid

Geil, vielen Dank für die Kommentare.
Jetzt verstehe ich zumindest, warum data:[] richtig sein muss.

Wir erstellen quasi ein Objekt mit Loch und rufen es später auf und 
füllen das Loch mit den Daten :-)

Leider passiert das anscheinend noch nicht bei mir.
Die Seite bleibt weiß, bis auf den Text " initial data".

Die Dateh aus der PHP Datei sehen so aus:
1
[{"date": "2019-11-05 13:34" , "wama_1":193, "wama_2":238},{"date": "2019-11-05 13:34" , "wama_1":201, "wama_2":238},{"date": "2019-11-05 13:34" , "wama_1":202, "wama_2":234},......]

Und da es ja funktioniert, siehe ganz oben, sollten die ja eigentlich 
richtig sein, oder?



Danke nochmal und Gruß

Kolja

von sid (Gast)


Lesenswert?

Naja solange da "initial data"
da steht wurde getdata() nicht aufgerufen..
ich nehme an das Ding läuft in deinem Localhost (xampp oder so?)
Also schau mal bitte in deinem Browser im javascript error log..
vielleicht hab ich n Tippfehler drin.

der ursprüngliche getdata() Aufruf ist so wie er ist nicht sonderlich 
schön,
könnte ja sein, dass noch nicht alles geladen ist, dann läuft er ins 
Leere,
deswegen ist es schöner ihn in ein "onready" zu packen,
oder manchmal schlicht in den footer der html zu setzen.
In dem Fall sollte es mMn gehen,
öffne einfach mal die Konsole im Browser
und gib dort nochmal getdata(); ein (entertaste nicht vergessen)
wenn dann immernoch initial data da steht hab ich bestimmt n TippFehler 
drin ;)

oder falls Du's online hast, gib mir mal den Link dann schau ich selbst 
wo's klemmt.

Es kann auch sein, dass "setData" die JSON Objekte nicht parsed;
ich hab nichteinmal in den Quelltext von Morris geguckt um ehrlich zu 
sein.
Ich hätte behauptet er tut

Falls nicht, muss man sich halt nen Parser bauen,
das ist aber auch relativ banal
1
function getdata()
2
{
3
  $.getJSON("data1.php?n=1500", function(data)
4
  {
5
    // ein neues leeres Array
6
    var parseddata=[];
7
    for(i=0; i< data.length; i++)
8
    {
9
        // das Array mit JSON geparsten Objekten füllen
10
        parseddata.push(JSON.parse(data[i])); 
11
    }
12
    Mgraph.setData(parseddata); // und dieses nun volle Array dann dem graphen zuschustern
13
    nReloads++; 
14
    $('#reloadStatus').text(nReloads + ' reloads');
15
    setTimeout(getdata,5000);
16
  });
17
}
Achte darauf dass ich das MorrisGraph Objekt einmal "Mgraph"
und zuletzt nur "graph" genannt hatte (letzteres ist allerdings mit dem 
html div leicht verwirrend, deswegen macht ein aussagekräftigere Name 
Sinn)

von sid (Gast)


Angehängte Dateien:

Lesenswert?

Aaalso

ich hab das Morris Dingen jetzt mal geladen und in meinen localhost 
geschoben

wenn ich Deine data1.php richtig verstanden habe
rufst Du 1500 datenpunkte ab,
das läuft in meinem Browser nicht, der morrisgraph braucht länger zum 
errechnen der Grafik als 5Sekunden, damit bleibt das Dingen weiss
500 stellt kein Problem dar, ab 1000 wird's schwierig bei meiner alten 
xp Kiste hier.

Also mein Test mit nur 150 Datenpunkten.

ich hab ne php gebastelt die zufällige Nummern erzeugt
und die Sekunden mit Übertragen im Datum damit Morris nicht alle Punkte 
auf eine Position der Zeitachse malt ;)

Ich denke Du kannst dem Beispiel folgen
(einfach beides in den example-Unterordner kopieren)

von Kolja L. (kolja82)


Lesenswert?

sid schrieb:
> Naja solange da "initial data"
> da steht wurde getdata() nicht aufgerufen..
> ich nehme an das Ding läuft in deinem Localhost (xampp oder so?)
> Also schau mal bitte in deinem Browser im javascript error log..
> vielleicht hab ich n Tippfehler drin.

Nabend

Ne, das ganze läuft schon im Internet, aber ioch würde die URL ungerne 
öffentlich machen.

Die Konsole gibt mir aber folgende Fehlermeldung aus:

TypeError: Morris.Line is not a constructor

Und das auch mit deinem neuene Beispiel.

Warum läuft das denn bei dir und bei mir nicht?

Habe W10 und n aktuellen Firefox, daran sollte es nicht scheitern.

Danke für die ganze Unterstützung :-)

von sid (Gast)


Lesenswert?

aktuell heisst nicht immer besser, aber das ist ein gaaanz anderes Thema 
;)

Also, kein Konstruktor heisst, morris.js ist nicht korrekt eingebunden;
möglicherweise fehlt jquery.
oder liegt in falscher version vor (das reicht manchmal)

ich kann ohne nachzusehen leider keine "korrekte" Antwort geben,
und wenn Du die url nicht teilen möchtest müssen wir eben anders an die 
Sache ran.

Also mach bitte mal folgendes:
lade diese zip:
https://github.com/morrisjs/morris.js/archive/0.5.1.zip


Entpacke das in einen Order in deinem webarchiv...
sagen wir "var/www/morristest/"

Nun kopiere den Inhalt meines zip Archivs von oben
nach "/var/www/morristest/examples"
[nichts in den Dateien ändern bitte, schau nach, tut nichts böses ;)]

Und zu guter letzt rufe
<deineurl>/morristest/data1.html im Browser auf

Öffne die Browser Konsole (STRG+UMSCHALT+J)
und mach mir von der Seite und der Konsole nen Screenshot bitte
( deine url kannste nachträglich im grafikprogramm schwärzen wenn Du 
magst,
mich interessiert der exakte Wortlaut und was wirklich zu sehen ist)

von sid (Gast)


Lesenswert?

oh.. es muss natürlich <deineurl>/morristest/examples/data1.html heissen 
..

von Kolja L. (kolja82)


Lesenswert?

Moin

Licht am Ende des Tunnels :-)

sid schrieb:
> <deineurl>/morristest/data1.html im Browser auf

Das funktioniert!
Und wenn ich dann einfach meine echen Daten einbinde funktioniert es 
auch noch :-)

Aber wenn ich das Ganze in mein Stammverzeichnis lege, ging es (trotz 
angepasster Pfade) zuerst nicht.

Der Unterschied und vielleicht die Erklärung für das mMn etwas 
mysteriöse Verhalten:

SSL

Mit https gehts nicht, mit http gehts(! :-).

Die Domain erzwingt kein SSL, meine Stammverzeichnis ist mit https im 
Browser gespeichert, den Pfad zu deinem Beispiel habe ich händisch 
getippt.

Ich suche mal nach einer Lösung.


Eins noch:
Wenn die Seite aufgerufen wird, erscheint der JS Code solange, bis die 
1500 Datensätze geladen worden sind (habe das Timeout einfach auf 10s 
gestellt ;-).
Bekommt man das noch relativ einfach weg?
Vielleicht sogar durch so ein sich drehendes Rad?

Lieben Gruß und besten Dank

Kolja

von Kolja L. (kolja82)


Lesenswert?

Das ging schnell, die eingebundenen Dateien müssen über https aufgerufen 
werden:

<script 
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>;
<script 
src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>;


Leg die mal lokal ab.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.