Frank S.: AJAX ResponseText und JSON

Hallo zusammen,

Ich hab da mal wieder seit Stunden ein Problem, was mich bald wahnsinnig macht.

Ich will eine Anwendung bauen, die über AJAX Datensätze alle 2 Sekunden ausliest und entsprechend aktualisiert.
Die PHP-Datei sieht wie folgt aus:

<?php  
  
  require_once('php/datenbank.inc.php');  
  //require_once('php/JSON/JSON.php');  
  
  
  $i  = $_POST["i"];  
  $db = new DB();  
  
  
  $db->query("SELECT `name`,`vorname`  
              FROM   `t_spieler`  
              WHERE  `t_spielerID`='$i'");  
  
  $db->next_record();  
  
  $name    = $db->f("name");  
  $vorname = $db->f("vorname");  
  
  $result = array("nachname"=> $db->f("name"),  
                  "vorname" => $db->f("vorname"));  
  
  echo json_encode($result);  
  
  
?>

Die Datei, die de AJAX-Aufruf ausführt, sieht so aus:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
<html>  
  <head>  
    <title>TEST</title>  
    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1">  
    <script src="js/json2.js" type="text/javascript"></script>  
    <script src="js/prototype.js" type="text/javascript"></script>  
  </head>  
<body>  
  
<script>  
  
	function getData() {  
		  
		var response = new Ajax.PeriodicalUpdater(  
			'ziel',  
			'execute.php', {  
				method: 'post',  
				parameters: 'i=' + document.hier.versteckt.value,  
				frequency: 2,  
          onSuccess: function() {  
            var antwort = response.responseText.evalJSON();;  
  
            alert('OK');  
  
            feld1.innerHTML =  antwort.nachname;  
            feld2.innerHTML = 'aktualisiert: ' + Date();  
          }  
			 }  
    );  
 }  
  
</script>  
 <input type="submit" onclick="getData()">  
<p>  
  
<div style="width:400px;" id="ziel"></div>  
<div id="feld1"></div>  
<div id="feld2"></div>  
  
<form name="hier">  
<input type="text" name="versteckt" value="12" />  
</form>  
  
</p>  
  
  
</body>  
</html>

Das Decodieren des JSON Return-Werts funktioniert allerdings nicht. Das Skript bricht einfach an der Stelle ab ohne wenigstens einen Fehler zu liefern. Das alert kann ich bereits nicht mehr sehen!?!

Kann mir da bitte jemand helfen?

Wenn ich statt responseText einfach den String in den Code kopiere, der in das div "ziel" geschrieben wird, funktioniert es.

Gruß,
Frank

  1. Das Decodieren des JSON Return-Werts funktioniert allerdings nicht. Das Skript bricht einfach an der Stelle ab ohne wenigstens einen Fehler zu liefern.

    Du weißt auch wo du die JS Fehler findest?

    Struppi.

    1. Du weißt auch wo du die JS Fehler findest?

      Hab das Problem gefunden: Die AJAX-Funktion liefert den Parameter erst NACH dem ersten Durchlauf, so dass man innerhalb der Funktion den Return-Wert nicht überprüfen oder zerlegen kann - super find ich, dass der dann einfach an der Stelle abbricht ohne wenigstens irgendeine Rückmeldung zu geben.

      1. Die AJAX-Funktion liefert den Parameter erst NACH dem ersten Durchlauf

        Wieso? Was steht denn in »antwort« beim bei der ersten Ausführung des onSucess-Callbacks?

        Bitte lade die Dateien mal irgendwo hoch und baue ein funktionsfähiges Beispiel, damit wir uns das ansehen können.

        Mathias

        1. Wieso? Was steht denn in »antwort« beim bei der ersten Ausführung des onSucess-Callbacks?

          In myAjax.responseText steht bei jedem Durchlauf undefined.

          Bitte lade die Dateien mal irgendwo hoch und baue ein funktionsfähiges Beispiel, damit wir uns das ansehen können.

          Wie soll ich ein funktionsfähiges Beispiel hochladen wenn mein Problem ist, dass es nicht funktioniert? =)

          1. » Wieso? Was steht denn in »antwort« beim bei der ersten Ausführung des onSucess-Callbacks?

            In myAjax.responseText steht bei jedem Durchlauf undefined.

            Woher kommt auf einmal myAjax? Es macht wenig Sinn, wenn wir uns Gedanken machen über Code der so bei dir nicht existiert.

            Du hast meine Frage auch nicht beantwortet, du weißt wo die Fehlermeldungen stehen?

            » Bitte lade die Dateien mal irgendwo hoch und baue ein funktionsfähiges Beispiel, damit wir uns das ansehen können.

            Wie soll ich ein funktionsfähiges Beispiel hochladen wenn mein Problem ist, dass es nicht funktioniert? =)

            Dann könnten wir vielleicht rausfinden warum es nicht funktioniert.

            Struppi.

            1. Woher kommt auf einmal myAjax? Es macht wenig Sinn, wenn wir uns Gedanken machen über Code der so bei dir nicht existiert.

              Ups, ich hatte zwischenzeitig den Namen des Objekts geändert, hieß vorher response. Ich hab mal genau den Quelltext, wie ich ihn oben gepostet hab auf den Server geladen, zu finden unter http://liveticker.tt-news.de/test.php

              Da sieht man dann, dass der PeriodicalUpdater funktioniert und auch das Rückgabefeld befüllt wird mit dem JSON String, die onSuccess Funktion läuft allerdings nicht, da nicht einmal der alert ausgeführt wird.

              Du hast meine Frage auch nicht beantwortet, du weißt wo die Fehlermeldungen stehen?

              Ich dachte, die stehen immer im Internet Explorer wenn etwas im Skript schiefgeht bzw. in Firebug - hab aber weder in dem einen noch in dem anderen einen Fehler gefunden.
              Sollte der Browser nicht automatisch alle Skriptfehler anzeigen, wenn er so eingestellt ist?

  2. $i  = $_POST["i"];

    $db->query("SELECT name,vorname
                  FROM   t_spieler
                  WHERE  t_spielerID='$i'");

    Kleiner Hinweis am Rande: Das hier riecht nach SQL Injection, validiere die Eingabedaten (z.B. mittels [mysql_real_escape_string](http://www.php.net/manual/de/function.mysql-real-escape-string.php).  
      
    
    > Die Datei, die de AJAX-Aufruf ausführt, sieht so aus:  
    > ~~~javascript
    
    <script src="js/json2.js" type="text/javascript"></script>  
    
    >     <script src="js/prototype.js" type="text/javascript"></script>
    
    

    Was steckt hier an Code drin?

      var response = new Ajax.PeriodicalUpdater(  
      	'ziel',  
      	'execute.php', {  
      		method: 'post',  
      		parameters: 'i=' + document.hier.versteckt.value,  
      		frequency: 2,  
    

    onSuccess: function() {
                var antwort = response.responseText.evalJSON();;

    alert('OK');

    feld1.innerHTML =  antwort.nachname;
                feld2.innerHTML = 'aktualisiert: ' + Date();
              }
    }
        );

    Was macht Ajax.PeriodicalUpdater?

    Das Decodieren des JSON Return-Werts funktioniert allerdings nicht. Das Skript bricht einfach an der Stelle ab ohne wenigstens einen Fehler zu liefern. Das alert kann ich bereits nicht mehr sehen!?!

    Das kann eigentlich nur dann sein, wenn die Funktion, in der das alert steht, nicht ausgeführt wird.

    --
    Reden ist Silber, Schweigen ist Gold, meine Ausführungen sind Platin.
    Self-Code: sh:( ch:? rl:( br:> n4:( ie:{ mo:) va:) de:> zu:} fl:| ss:| ls:~ js:|
  3. Hallo,

    es wäre schön, wenn Du nebenbei bemerken würdest, dass Du Prototypes Ajax-Objekte statt normalen XMLHttpRequest benutzt, dann verwirrst Du den armen Timo und andere nicht so und ersparst diesen die dummen Nachfragen und das Nichtbenutzen von Google. ;)

    var response = new Ajax.PeriodicalUpdater(
         'ziel',
         'execute.php',
                       {
             method: 'post',
    parameters: 'i=' + document.hier.versteckt.value,
    frequency: 2,
                           onSuccess: function() {
                               var antwort = response.responseText.evalJSON();;

    Du solltest ab besten nicht mit dem Ajax.PeriodicalUpdater-Objekt arbeiten, sondern das Response-Objekt nutzen, was bei allen Callback-Funktionen mitgeliefert wird. Ajax.PeriodicalUpdater ist ein erweitertes Ajax.Updater, welches ein erweitertes Ajax.Request ist. Somit gilt für den PeriodicalUpdater die gleichen Zugriffsvarianten wie für den Request. Unter anderem auch die, dass den Callbacks-Funktionen als erstes Argument ein Ajax.Response-Objekt geliefert kriegen, wenn sie aufgerufen werden. Sprich, Du schreibst das hier:

    ~~~javascript var updater = new Ajax.PeriodicalUpdater("ziel", "execute.php", {
                            method : "post",
                            onSuccess : function (response) {
                                var text = response.responseText.evalJSON()
                                // ...
                            }
                        });

      
    Um es noch mal zu wiederholen: Die Response wird an die Callback-Funktion onSuccess übergeben. Du dagegen greifst in Deiner Callback-Funktion auf das Ajax.PeriodicalUpdater-Objekt zu, dass Du in der globalen Variable response gespeichert hast. Dass dieses Objekt kein Attribut namens responseText hat und deswegen beim Zugriff auf Ajax.PeriodicalUpdater.resonseText immer undefined rauskommen muss, ist dann klar.  
      
      
    Tatsächlich kann man Dein Zeugs mit diversen Features von Prototype noch erweitern und damit besser machen. Du kannst z.B. JSON automatisch evaluieren lassen, wie Du im [Prototype AJAX Tutorial](http://prototypejs.org/learn/introduction-to-ajax) nachlesen kannst:  
      
      ~~~javascript
    new Ajax.PeriodicalUpdater("ziel", "execute.php", {  
          onSuccess : function (response) {  
              tueWasMit(response.responseJSON);  
          }  
      });
    

    Das zweite Argument für die Callback-Funktion existiert dann, wenn Du vom Server aus den für JSON richtigen MIME-Type, nämlich "application/json" mitschickst; schließlich verschickst Du ja JSON und nicht "text/html". In PHP kannst Du das meines Wissens mit dem Aufruf von [link:http://de.php.net/manual/de/function.header.php@title=header]('Content-type: application/json'); bevor Dein Skript irgendwelchen Code sendet. Bei kurzen JSON-Strukturen könnte man diese sogar in einem X-JSON-Header tun und Prototype reagiert auch darauf. Für mehr dazu und zu den Bedingungen des evaulierens, guck Dir mal die möglichen Optionen für Prototype-AJAX-Aufrufe an.

    parameters: 'i=' + document.hier.versteckt.value,

    Diese Zuweisung hier könnte darunter leiden, dass im Feld "versteckt" irgendwelche Werte stehen, die nicht richtig URL-kodiert sind. Prototype erwartet jedoch einen URL-kodierten String; wenn der falsch ist, ist die zusammengebastelte URL hinterher falsch. Aber Prototype nimmt dort auch gerne ein Objekt und bastelt daraus einen URL-kodierten String. Wenn Dein Framework das für Dich macht, sollte man es nutzen; meistens haben diese nämlich die Fehler, die man selber gemacht hat, schon hinter sich gelassen. Das sähe dann verkürzt so aus:

    ~~~javascript new Ajax.PeriodicalUpdater("ziel", "execute.php", {
              parameters : { "i" : [link:http://prototypejs.org/api/utility/dollar-f@title=$F("versteckt")] }
          }
      });

      
    Dir fällt bestimmmt auf, dass ich hier öfters auf Tippfaulheit den PeriodicalUpdater nicht in einer Variablen sichere im Gegensatz zu Deiner obigen irrigen response-Variable. Das geht, ist aber nicht so toller Stil. Besonders beim PeriodicalUpdater will man später auf den laufenden Updater zugreifen und eventuell dessen stop()-Methode aufrufen. Nur so nebenbei bemerkt.  
      
      
    
    > feld1.innerHTML =  antwort.nachname;  
    > feld2.innerHTML = 'aktualisiert: ' + Date();  
      
    In Deinem Skript hast Du nirgends die Variablen feld1 und feld2 definiert. Wenn Du auf die divs anhand ihrer ID zugreifen willst, gibt es doch klassisches documentGetElementById() bzw in Prototype die Funktion [$(id)](http://prototypejs.org/api/utility/dollar), die die Elemente auch zu Prototype-Elementen upgraded:  
      
      ~~~javascript
    $("feld1").replace(antwort.nachname);  
      $("feld2").replace("Aktualisiert: "  Date());
    

    Zwei Gedanken noch:

    • PeriodicalUpdater ist wie sein Cousin Updater eigentlich dafür da, die ganze Response direkt in ein Element im Dokument einzuhängen. Derzeit scheinst Du das ja mit div#ziel zu machen; langfristig scheint es Dir lieber zu sein, wohl nur die Einzelfelder zu benutzen. Eventuell wäre es dann sinniger, mit dem PeriodicalExecuter in regelmäßigen Abständen jeweils ein Ajax.Request-Objekt loszujagen, anstatt, dass der Ajax.PeriodicalUpdater sich dann auf die Suche nach einem von Dir nicht gewünschtem Element macht.

    • Äh. Ich bin kein PHP-Mensch, aber wenn ich Dein Skript so querlese, dann macht das einen Datenbankquery, nimmt davon den ersten Datensatz, ver-JSON-t und verschickt diesen. Und dann ist das Ende des Skriptes. Wenn dann zwei Sekunden später ein neuer Request kommt, wird das Skript neu gestartet, der Query neu gestellt und wieder nur der erste Datensatz geholt, weil die alte Datenbankverbindung beim Beenden des alten Skript-Aufrufes verschwunden ist. Für einen Liveticker fehlt da wohl das „live“, es sei denn Deine Vorstellung von „live“ ist es, immer den ersten Datensatz darzustellen. Aber vielleicht habe ich auch missverstanden, was Du da machen willst. Und Du solltest Dir auch mal mysql-real-escape-string() angucken, wenn Du nicht willst, dass da jemand über den Parameter i irgendwelche bösen SQL-Befehle einschmuggelt. Ich hab von PHP keine große Ahnung, aber wenn Dein datenbank.inc.php dieses PearDB ist, solltest Du dort mal in der Dokumentation gucken. Da scheint eine Möglichkeit zu geben, in SQL-Queries Platzhalter zu benutzen, die dann mittels Array befüllt und hoffentlich dabei maskiert und damit vor SQL Injection geschützt werden.

    Tim