Tutorial: Ajax Ladeanzeige wie bei Google mit JBoss RichFaces

Einer der Grundsätze bei der Verwendung von Ajax ist, dass jeder Ajax Request, der von Belang für den Anwender ist, visuell in irgendeiner Form angezeigt werden muss. Ajax Requests sind bekanntlich asynchron und laufen im Hintergrund, so dass der Anwender diesen nicht wie einen normalen Request im Browser mitbekommt bzw. dargestellt bekommtn. Somit wird dieser sich doch sehr wundern oder es nicht nachvollziehen können, wenn auf einmal ein Teil der Seite ausgetauscht, verschwunden oder neu hinzugefügt wurde. So kommen wir zu der “Ajax Ladeanzeige” oder “Ajax Loading Indicator” wie es oft genannt wird.

Ein meiner Meinung nach sehr schönes Beispiel für eine solche Ajax Ladeanzeige bietet Google. Wer schon mit Google Anwendungen gearbeitet hat dem wird diese globale Ladeanzeige mittig oben im Browser durchaus bekannt sein:

Der Vorteil einer solchen globalen Ladeanzeige liegt darin, dass sie dem Anwender bekannt ist. Hat er sich einmal daran gewöhnt, dass sie mittig oben zu finden ist fällt sie ihm bei jedem erneutem Anzeigen eher ins Auge und er sieht sofort, dass eine Aktion im Hintergrund passiert.

Eine solche Ladeanzeige kann man sehr einfach mit Hilfe der JBoss RichFaces erstellen und in einer eigenen Anwendung für die Statusanzeige eines jeden Ajax Requests verwenden. Wie genau das angestellt wird ist im folgenden zu lesen.

Ajax4Jsf Status Komponente

Um unter Verwendung der JBoss RichFaces auf einen aktiven Ajax Request, der zum Beispiel durch einen a4j:commandButton oder a4j:commandLink ausgelöst wurde zu reagieren und einen Status anzuzeigen dient die RichFaces Komponente “a4j:status“. In der einfachsten Variante wird die a4j:status Komponente wie folgt erstellt:

<a4j:status id="commonstatus"  startText="In progress..." stopText="Finish"/>

Sobald ein Ajax Request durch einen CommandLink oder CommandButton gestartet wird reagiert diese Statuskomponente darauf und zeigt den Text “Wird geladen…”. Ist der Ajax Request beendet (und natürlich auch schon vor dem Ajax Request) wird der Text “Fertig” angezeigt.

Diese Komponente hat noch weitere Möglichkeiten genutzt zu werden. So können zum Beispiel ganze Html Blöcke ein und ausgeblendet werden anstatt nur ein Text, wie auch auf der RichFaces LiveDemo zu sehen.

Ladeanzeige Div erstellen und mit CSS formatieren

Wir wollen aber in diesem Tutorial eine globale Ajax-Ladeanzeige verwenden und benötigen somit noch ein Html Fragment, welches uns das Ladebild und Text mittig oben im Browserfenster anzeigt.
Dazu wird ein div Element, dass mit CSS formatiert wird, erstellt. Dieses div Block muss natürlich auf jeder Seite vorhanden sein, die einen Ajax Request auslösen kann. Ich arbeite bei bei der Entwicklung einer JSF Anwendung immer mit Facelets und somit ist das verwendete Facelets Template (template.xhtml) der richtige Platz um diesen Code unterzubringen:

<div id="globalStatusDiv" style="display:none;">
  <img src="#{baseUri}/img/ajax-loader.gif" alt="Loading" />&#160;Loading..
</div>

Die dynamische Ajax Loading Grafik kann man sich bequem auf ajaxload.info generieren lassen und passend zu dem obigen Code Beispiel im Ordner img ablegen. Man kann an dieser Stelle natürlich auch wie bei JSF gewohnt die h:graphicImage Komponente verwenden.
Hier noch der Ausschnitt wie der Ausdruck #{baseUri} ausgewertet wird:

<html xmlns="http://www.w3.org/1999/xhtml"
  ...
  xmlns:c="http://java.sun.com/jstl/core">
  ...
  <c:set var="baseUri" value="#{facesContext.externalContext.requestContextPath}"/>
  ...

Dazu dazugehörige CSS wird separat in einer externen CSS Datei abgelegt:

* html body { margin: 0; overflow-y: hidden; padding: 0; }

#globalStatusDiv {
  position: fixed;
  top: 0;
  text-align: center;
  margin-left: auto;
  margin-right: auto;
  left: 50%;
  width: 100px;
  margin-left: -50px;
  height: 25px;
  line-height: 25px;
  background-color: #FFF;
  padding: 2px 15px 2px 10px;
  color: #000;
  font-family: Verdana,Arial,Helvetica,sans-serif;
  font-size: 12px;
  font-weight: bold;
  z-index: 10000;
  border: 1px solid #BBB;
  border-top: none;
}

#globalStatusDiv img {
  vertical-align: middle;
}
Ajax4Jsf Status Komponente anpassen

Nachdem nun das “GlobalStatus” div Element samt CSS Angaben definiert sind muss nun noch die Ajax4Jsf Status Komponente angepasst werden. Es soll nun nicht mehr der Text angezeigt werden sondern das “GlobalStatus” div ein- und ausgeblendet werden. Dazu bedienen wir uns der JavaScript Bibliothek jQuery, die mit den RichFaces gleich mitgeliefert wird. Damit das Beispiel funktioniert und wir die von den RichFaces mitgelieferte jQuery JavaScript Bibliothek nutzen können müssen wir die RichFaces “Script Load Strategy” von “DEFAULT” auf “ALL” in der web.xml abändern:

  <context-param>
    <param-name>org.richfaces.LoadScriptStrategy</param-name>
    <param-value>ALL</param-value>
  </context-param>

Damit werden immer alle JavaScript Resroucen auf jeder Seite geladen und nicht nur wenn eine RichFaces Komponente sie benötigt. Es bestehen hier natürlich mehrere, sogar viel bessere Lösungen als diese hier verwendete.

Anschließend die Ajax Status Komponente wie folgt abändern.

<a4j:status id="globalStatus" forceId="true" layout="none"
  onstart="jQuery('#globalStatusDiv').fadeIn('fast')"
  onstop="jQuery('#globalStatusDiv').fadeOut('slow')" />

Es wird nichts weiter gemacht als das “GlobalStatus” div-Block bei einem Ajax Request sichtbar zu machen (fadeIn()) und nach dem Request wieder auszublenden (fadeOut()). Mehr zu diesen Funktionen auch in der offiziellen jQuery Effects Dokumentation.

Beispielseite mit Ajax Request

Nun fehlt nur noch eine Kleine Beispielseite. Dazu habe ich mir eine klein Bean mit einem Attribut erstellt, welcher mit Ajax gesetzt und anschließend ausgegeben werden wird.

Hier die Bean – ich verwende in diesem Beispiel JBoss Seam, daher wird mit der @Name Annotation gearbeitet. Die herkömmliche Bean Konfiguration durch die faces-config.xml funktioniert natürlich genauso.

package de.javathreads.helloworldseamprojekt.session;

import org.jboss.seam.annotations.Name;

@Name("editBean")
public class EditBean {

  private String name;

  public void setName(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}

Und hier der interessantere Part – der Ausschnitt aus JSF Seite:

<h:form>
  <h:inputText value="#{editBean.name}" />
  <a4j:commandButton value="Speichern" reRender="viewNamePanel" status="globalStatus" />
</h:form>

<a4j:outputPanel id="viewNamePanel">
  Name: #{editBean.name}
</a4j:outputPanel>

Wichtig ist, dass bei der a4j:commandButton Komponente das Attribut status gesetzt ist. Mit diesem Attribut wird die a4j:status Komponente angegeben, die für diesen Ajax Request anzeigen soll.

Das Ergebnis sieht dann wie folgt aus. Beim absenden des Ajax Requests, hier durch Drücken das “Speichern” Buttons, erscheint die Loading Anzeige:

Ist der Request fertig und wurde der Ausgabe Bereich (Name anzeigen) neu gerendert verschwindet auch die Ladeanzeige:

Habe vor einiger Zeit Jing gesehen und wollte es schon immer mal ausprobieren – hier also das “live” Beispiel:

Das Layout des Statusanzeige oder Loadingblocks kann man natürlich komplette mit Hilfe von Html und CSS selbst bestimmen. Bei OpenWishes haben wir zum Beispiel eine gelbe Hintergrundgrafik gewählt. Und weil Jing soviel Spass gemacht hat kommt hier das Beispiel aus OpenWishes. Hier erscheint die Ladeanzeige während wenn man sich einen neuen Wunsch erstellen möchte und dazu die Produktwebseite angibt die anschließend geparst werden muss um die Informationen zu erhalten:

Tags: , , , ,

Wenn du Fragen oder Anregungen zum Post hast, dann hinterlasse doch einen Kommentar oder wenn du weiterhin Artikel von Javathreads lesen möchtest, dann abonniere den RSS Feed und sehe direkt in deinem Feed Reader die nächsten Artikel.

Ähnliche Artikel, die dich interessieren könnten:
Kommentare

Hi,

Danke erstmal für deinen Artikel es sieht wunderbar aus.
Ich habe auch andere Ajax kompnente in meiner Form die jetzt nicht mehr machen was sie sollen? Was kann ich ändern

Gruß Jan

Hi Jan, leider sind deine Infos zu knapp um eine genaue Antwort zu geben. Aber vielleicht hilft der Hinweis, dass man auch mehrere Formulare in einer Seite haben darf (allerdings nicht geschachtelt!) und dass man Regionen die bei einem Ajax Request beachtet werden sollen mit a4j:region eingrenzen kann. Aber generell sollte es auch ohne diese Einschränkungen funktionieren.

Ich verwende diese globale Ladeanzeige auch mit komplexen Seiten auf denen viele Ajax Komponenten.
Vielleicht kannst du dein Fehlverhalten genauer beschreiben.
Gruß Markus

Nettes Tutorial, danke dafür.
Allerdings ist sind Text-/Hintergrund-/Linkfarben so ungünstig gewählt (grau auf grau), dass ich den Text nicht lesen konnte ohne ihn zu markieren oder Augenkrebs zu bekommen. Oder klappt einfach was mit dem CSS nicht ? (Firefox).

Hi Fabian,
bei mir sieht es im Firefox (3.x) soweit ok aus (siehe Screenshot: http://javathreads.de/files/JavathreadsScreenshotAjaxLoadingTutorial.png). Die letzten beiden Zeilen im Screenshot sind markiert, was kaum rauskommt und somit nochmal angepasst werden muss.

Welchen Firefox verwendest du genau? Evtl. kannst du mir mal nen Screenshot machen und mir per E-Mail senden? Oder ich kann das mit dem von dir verwendeten Firefox nachstellen.
Wenn es wirklich ein Problem gibt sollte das schnell behoben werden!

Hallo.

Schönes Tutorial. Eine Frage hätte ich noch:
Wie verhinderst du, dass die Ladeanzeige beim ersten Aufruf der Seite erscheint? Bevor überhaupt der Speichern Button betätigt wird?

Hoi,
damit die Ladeanzeige initial nicht angezeigt wird, wird CSS display:none verwendet:
<div id=”globalStatusDiv” style=”display:none;”>

Hi,
dank deiner Beschreibung ist dieses Feature schnell und schön umzusetzen. Hat Spass gemacht. Mehr Spaß bringen hier aber auf jeden Fall die Kommentare: “Vielleicht kannst du dein Fehlverhalten genauer beschreiben.” =)
Gruß Arend

Klappt super!
Vielen Dank!

Hallo!

Sehr gutes Tutorial.
Habe da jedoch eine Frage:
Kann der angezeigte Ladebalken (oder was auch immer man nimmt) von einem Screenreader erkannt werden, d.h. bekommt ein blinder Benutzer mit, dass dort etwas passiert?

Danke und viele Grüße
Sebastian

Hinterlasse einen Kommentar