evnt: Welches Event befragen?

Moin;

Die Anwendung ist ein einfacher Chat mit Ajax. Dem Request wird eine Liste derjenigen Nachrichtennummern mitgegeben, die bereits ins DOM (Chat-Formular) gerendert sind. Somit werden in der Response nur diejenigen Nachrichten gesendet, welche der Browser noch nicht hat. Das ist der Plan.

Hier jedoch liegt ein kleines Problem, denn es kann passieren, dass ein Request rausgeht, der noch nicht alle neuen Nachrichten(nummern) kennt, weil das Rendern ins DOM noch nicht fertig ist. CODE zu:

        var input = document.createElement("input");
        input.type = "hidden";
        input.name = 'having';
        input.value = messages[i]['lfdnr'];
        chatform.appendChild(input);

Und beim Request werden alle <input name="having"> befragt, was die besagte Liste der Nachrichtennummern ergibt. Wenn jedoch die Nachricht noch nicht bekannt ist, kriegt sie der Browser mehrfach, was beim schnell aufeinanderfolgenden Senden passiert.

Ich sollte jedoch vor jedem Request feststellen, ob das Rendern ins DOM.chatform fertig ist. Dankbar für eine Idee zur Vorgehensweise.

Viele Grüße, Event-Manager

  1. Tach!

    Hier jedoch liegt ein kleines Problem, denn es kann passieren, dass ein Request rausgeht, der noch nicht alle neuen Nachrichten(nummern) kennt, weil das Rendern ins DOM noch nicht fertig ist. CODE zu:

    Ich würde nicht immer wieder das DOM rauf und runter abklappern, um zu ermitteln, welche Daten ich habe. Stattdessen nähme ich eine eigene Datenhaltung, in die die Chatnachrichten eingefügt werden. Das Rendern überließe ich AngularJS. (Das kann auch den Ajax-Kram erledigen.)

    Meine Datenhaltung sähe so aus, dass die Nachrichten mit einem Timestamp versehen, und diesen als Eigenschaftsname verwendend, in ein Objekt eingefügt werden. Damit ergibt sich sowas wie ein assoziatives Array. Die Keys kann man beim Ausgeben sortieren, um die Nachrichten in der richtigen Reihenfolge anzuzeigen. Der Timestamp muss aber höher als bis Sekunden aufgelöst werden, denn die Wahrscheinlichkeit, dass zur selben Sekunde mehr als eine Nachricht eingehen, ist nicht besonders nahe bei Null. Millisekunden reichen vermutlich, ansonsten muss da eben ein anderer eindeutiger Wert angehängt werden. Mit dieser Datenhaltung wäre es egal, wenn Nachrichten mehrfach kämen, weil sie dann in dem Objekt einfach überschrieben würden.

    Das Rendern dann ist ein Einzeiler mit AngularJS, plus das HTML für eine Chatnachricht.

    dedlfix.

    1. Tach!

      Hier jedoch liegt ein kleines Problem, denn es kann passieren, dass ein Request rausgeht, der noch nicht alle neuen Nachrichten(nummern) kennt, weil das Rendern ins DOM noch nicht fertig ist. CODE zu:

      Ich würde nicht immer wieder das DOM rauf und runter abklappern, um zu ermitteln, welche Daten ich habe. Stattdessen nähme ich eine eigene Datenhaltung,

      Doppelte Datenhaltung ist schlecht. Selbst wenn ich das so machen würde, es würde das Problem nicht beheben. Der Laufzeitfehler wird zwar kleiner, aber er bleibt, weil sich asynchrone Requests überschneiden..

      Mit Stand der Dinge tritt das geschilderte Problem sehr selten auf, ist aber immerhin möglich. Praktisch wird auch nicht das DOM rauf und runter abgeklappert sondern nur das Formular serialisiert um an die Liste der Nachrichtennummern zu kommen für den Request. Und wenn eine Response neue Nachrichten bringt, werden einfach nur <input>-Felder mit gleichem Namen an das Formular gehängt (mehrere gleichnamige Parameter bilden dann serverseitig ein Array mit den Valü's). Mehr Aufwand ist nicht gerechtfertigt.

      mfg

      ... und es wurmt mich trotzdem ;)

      1. Tach!

        Ich würde nicht immer wieder das DOM rauf und runter abklappern, um zu ermitteln, welche Daten ich habe. Stattdessen nähme ich eine eigene Datenhaltung,

        Doppelte Datenhaltung ist schlecht.

        Das ist keine doppelte Datenhaltung. Das ist eine Datenhaltung in einem verarbeitbaren Format. Dass darin enthaltenen Daten zwecks Ausgabe oder anderer Verarbeitung an anderen Stellen vervielfältigt werden müssen, steht auf einem anderen Blatt. Gehalten zum Zwecke der Verwaltung werden sie nur einmal, in dem speziellen Objekt. Sie in der Gegend zu verteilen und dann diese Gegend ständig abzusuchen, das ist schlecht.

        Selbst wenn ich das so machen würde, es würde das Problem nicht beheben. Der Laufzeitfehler wird zwar kleiner, aber er bleibt, weil sich asynchrone Requests überschneiden..

        Es kommt zu keinem Fehler, weil jeder Datensatz zum einen eindeutig gekennzeichnet ist, und zum anderen sich durch diese eindeutige Kennzeichnung in einem quasi Unique Index des datenhaltenden Objekts keine Verdopplungen ergeben.

        Mit Stand der Dinge tritt das geschilderte Problem sehr selten auf, ist aber immerhin möglich. Praktisch wird auch nicht das DOM rauf und runter abgeklappert sondern nur das Formular serialisiert um an die Liste der Nachrichtennummern zu kommen für den Request. Und wenn eine Response neue Nachrichten bringt, werden einfach nur <input>-Felder mit gleichem Namen an das Formular gehängt (mehrere gleichnamige Parameter bilden dann serverseitig ein Array mit den Valü's). Mehr Aufwand ist nicht gerechtfertigt.

        Das liest sich jedenfalls nicht besonders gut. Warum müssen denn die Daten in ein Input mit allem drum und dran, wenn ein div genügt? Und wenn du wirklich keinen großen Aufwand haben möchtest, dann nimm AngularJS, verringere deinen Entwicklungsaufwand und lass den Aufwand von Angular erledigen.

        Die Ausgabe sähe ungefähr so aus:

        <body ng-app="chat">
        ...
        <section ng-controller="messages">
          <div ng-repeat="key in Object.keys(datenhaltung) | orderBy: 'toString()'">
            <span>{{datenhaltung[key].time| date:'HH:mm:ss' }}</span>
            <span>{{datenhaltung[key].sender}}</span>
            {{datenhaltung[key].text}}
          </div>
        </section>
        ...
        </body>
        

        Ein Controller könnte so aussehen:

        angular.module('chat', [])
        .controller('messages', ['$scope', '$http', function($scope, $http) {
          $scope.datenhaltung = {};
        
          var now = new Date().getTime();
          $scope.datenhaltung[now] = {
            time: now,
            sender: 'System',
            text: 'Willkommen im Chat'
          };
        
          var poll = function() {
            // nach einer Sekunde
            $timeout(function () {
              $http.post('/someUrl', {keys: Object.keys($scope.datenhaltung)}).
                then(function(response) {
                  // Datenhaltung befüllen
                  // messages ist ein Array mit Message-Objekten wie oben bei der Begrüßung
                  angular.forEach(response.data.messages, function (message) {
                    $scope.datenhaltung[message.time] = message;
                  });
                }, function(response) {
                  // hier Code für den Fehlerfall
                });
              poll();
            }, 1000);
          };
        
          poll();
        }]);
        

        Das ist nur ein ausbaufähiges Grundgerüst, da fehlt noch das Drumherum, wie das Einbinden der Angular-Bibliothek und das Behandeln der Nutzer-Eingaben. Der Code soll zeigen, wie einfach und übersichtlich es gehen kann. Ich hab ihn einfach nur so dahingeschrieben, ohne ihn zu testen. Noch schöner wäre er unter Verwendung einer websocketbasierten Kommunikation geworden. Damit ergibt sich auch nicht das Problem des ständigen Pollens und Vergleichens, welche Nachrichten noch fehlen. Die kommen dann in der Reihenfolge, wie sie am Sever eintrudeln. Dafür gibt es auch fertige Bibliotheken inklusive Beispielen im Netz. (Auf die Schnelle gefunden: ... but in the meantime I had a look at AngularJS and knocked up a quick chat app to see how easy it would be. The answer was “very”.)

        dedlfix.

        1. hi,

          Das ist nur ein ausbaufähiges Grundgerüst, da fehlt noch das Drumherum, wie das Einbinden der Angular-Bibliothek und das Behandeln der Nutzer-Eingaben. Der Code soll zeigen, wie einfach

          Um Gottes Willen, da ist ja das, was ich habe, von seiner Einfachheit ein Witz dagegen ;)

          Trotzdem hab ich nochmal nachgedacht. Serverseittig hab ich EIN Objekt für Request/Response. Nach demselben Pronzip könnte ich das auch clientseitig machen, also in JS ein Request/Response-Objekt (RO) zusammenstellen.

          Bei einer Response werden dann die Nachrichtennummern anstelle ins Formular, in ein Attribut des RO geschrieben, wobei dieses Attribut ein Array ist. Wenn RO dann einen Request feuert, wird das Array serialisiert und nicht das Form (wie bisher), ansonsten gehen die Nachrichtennummern so zum Server wie bislang.

          Das wäre Plan B und das probiere ich einfach mal aus, Versuch mach kluch ;)

        2. hi,

          Das liest sich jedenfalls nicht besonders gut. Warum müssen denn die Daten in ein Input mit allem drum und dran, wenn ein div genügt?

          In den Inputfeldern (hidden) stehen nur die Nummern der Nachrichten. Mir genügt eine Zeile, danach habe ich alle Nachrichtennummern für den Request:

          var input = input4form(0); // ähnlich jQuery serialize()      
          // als HTTP-Request Parameter 'having'
          

          Serverseitig erzeuge ich aus dem Parameter 'having' ein assoc. Array (Hash);

          my %having = $self->param('having') ? map{ $_ => $_ } $self->param('having') : ();
          

          Und habe damit auf dem Server alle dem Browser bekannten Nachrichtennummern. Jede Response ist, sofern neue Nachrichten aufm Server eingetroffen sind und der Browser die noch nicht kennt, eine zyklische Datenstruktur, praktisch ein großes Objekt mit kleinen Objekten. Sortiert wird nach der Nachrichtennummern:

          messages.sort( function(a,b){ return (a['lfdnr'] - b['lfdnr']) } );
          

          und dann gehts über eine Schleife, wo aus jedem Objekt die Attribute 'mesg', 'nickname', 'datetime' gelesen und damit eine neue Zeile (oben) im Browseer erzeugt wird. Das ist mein Grundgerüst ;)

          PS: Ich lasse die Nachrichtennummern im Formular. Mit einem globalen Array wirds auch nicht besser. Der Laufzeitfehler ist minimal, um den zu erzeugen, muss ein Chatter ca. 1000 Anschläge pro Minute schaffen ;)

          1. hi,

            Das liest sich jedenfalls nicht besonders gut. Warum müssen denn die Daten in ein Input mit allem drum und dran, wenn ein div genügt?

            In den Inputfeldern (hidden) stehen nur die Nummern der Nachrichten. Mir genügt eine Zeile, danach habe ich alle Nachrichtennummern für den Request:

            var input = input4form(0); // ähnlich jQuery serialize()      
            // als HTTP-Request Parameter 'having'
            

            Wobei das Formular ohnehin serialisiert werden muss damit es Nachrichten überhaupt bis zum Server schaffen. Unabhängig davon gibts noch ne völlig andere Lösung für die Nachrichtennummern, welche der Browser kennen sollte: Ebendie in der Session speichern und damit ist das alles serverseitig. D.h., dass die periodischen Anfragen, obs was Neues gibt, gar keinen Parameter mehr brauchen. Und die Session gibt es sowieso, weil sich der kleine Nick ja am Chat anmelden möchte.

            So, Schluss für heute ;) . ... ... . -. (danke Sam)

  2. Wie Turnschuhe vielleicht? Oder eher wie Lotenmäntel?

    Bitte mal um Hinweise ;)

    Dedlefix, ich hab dich nicht vergessen. Im Prinzip denken wir beide gans ähnlich. Danke fürs Mitdenken!

    mfg

    1. Hallo

      ##Krimskram

      Wie Turnschuhe vielleicht?

      Die heißen heute anders, sind es aber noch. Möchte nicht das Gesicht eines Angestellten eines Schuhgeschäfts sehen, wenn ich nach Turnschuhen frage. … oder doch?

      Oder eher wie Lotenmäntel?

      Lodenmäntel. Und ja, in gewissen Kreisen, in denen man nicht, nicht mehr jugendlich seiend, noch jugendlich aussehen möchte, sind die tatsächlich in.

      ##Chats

      Sie haben längst nicht mehr die Bedeutung wie noch vor 10 Jahren, als z.B. Pro7 seinen Chat im TV bewarb. Seitdem so ziemlich jeder ein Smartphone hat und dort den Messenger seiner Wahl benutzen kann, wird eher darüber gechattet, auch wenn das nicht so unmittelbar ist, wie in einem klassischen Chat-Programm.

      Es gibt aber Nischen – z.B. über den Planeten verteilte Entwicklergruppen – in denen Chats immer noch eine Rolle z.B. als Alternative zu Telefonkonferenzen spielen. Als Bonus gibt's das Gesprächsprotokoll automatisch dazu.

      Tschö, Auge

      --
      Es schimmerte ein Licht am Ende des Tunnels und es stammte von einem Flammenwerfer.
      Terry Pratchett, „Gevatter Tod“
      1. Lodenmäntel. ..

        Ahh, Lodenmäntel hats's geheiß' darin sah ich mit 20 aus wie ein Greis. Unrasiert, doch blutig jung, mein Lieblingssong was Aqualung ;)

        Danke Jethro Tull