JSF: Einfügen einer variablen Anzahl von Dateien

Außerhalb meines Tutorials, und daher entsprechend komprimiert, möchte ich an dieser Stelle über aktuelle Erfahrungen mit JSF berichten.

In einer WebApplikation kann sich der Anwender für diverse Dienste (Features) registrieren. Nach dem Einloggen erscheint eine Übersichtsseite, die ggf. zu einzelnen Features Informationen bereit stellt. Dazu verfügt die Anwendung über eine Bean, die als SessionController die gewählten Features via Fabrik instanziiert. Jedes Feature soll nun bei Bedarf selber einen Teil (Part) der Übersichtsseite zur Verfügung stellen. Da ein Part beliebig viele Komponenten umfassen darf, sind diese jeweils als <ui:composition> in einer eigenen Datei abgelegt. Die Idee war nun, via SessionController eine dynamische Liste der Dateinamen zur Verfügung zu stellen. Diese Liste sollte nun in einer Schleife <ui:repeat> abgearbeitet werden und mittels <ui:include> die Dateien einbinden:

<ui:repeat value="#{sessionController.parts}" var="part">
  <ui:include src="#{part}"/>
</ui:repeat>

Im SessionController existiert ein passende Methode

public List<String> getParts(){
    return parts;
}

Parts wiederum wird beim Registrieren der Features mit den Dateinamen gefüllt. Die Ausgabe jedoch bestand aus – Nichts.

Also, zur Kontrolle eine Ausgabe der Dateinamen eingebaut.

<ui:repeat value="#{sessionController.parts}" var="part">
  <h:outputText value="#{part}" style="background-color: green"/>
  <ui:include src="#{part}"/>
</ui:repeat>

Und siehe da, die Dateinamen werden korrekt ausgegeben.

Nun diese Variante, Zugriff auf das erste Element der Liste:

<ui:repeat value="#{sessionController.parts}" var="part">
  <h:outputText value="#{part}" style="background-color: green"/>
  <ui:include src="#{sessionController.parts[0]}"/>
</ui:repeat>

Nun wird der Inhalt der ersten registrierten Datei dargestellt – und sooft wiederholt wie es Dateieinträge insgesamt gibt. Eine Analyse des Verhaltens ergab, dass getParts jedoch nur einmal aufgerufen wird. Es scheint so, als ob der Inhalt von src="…" bereits vor Aufruf der Schleife ausgewertet wird. Das erklärt auch, warum im ersten Fall nichts ausgegeben wurde: Vor Ausführung der Schleife ist part nicht definiert.

Nutzt man jedoch anstelle von <ui:repeat> das Konstrukt <c:forEach> aus der JSP-Library, so funktioniert das Einfügen einer variablen Anzahl von Dateien völlig problemlos.

<c:forEach items="#{sessionController.parts}" var="part">
      <ui:include src="#{part}"/>
  </c:forEach>

Ein Bug in der Facelets-Implementierung?

 

 

 

 

3 thoughts on “JSF: Einfügen einer variablen Anzahl von Dateien”

  1. Hallo Michael,

    >Ein Bug in der Faclets-Implementierung?

    Nicht wirklich. Eher ein Design-Bug von Facelets. Das Problem ist hier, dass sich c:forEach und ui:repeat komplett unterschiedlich verhalten, viele JSF-developer das aber nicht wissen:

    c:forEach arbeitet auf Facelets-Ebene, d.h. wenn der Komponentenbaum erzeugt wird. Es scheint dabei gar nicht im Komponentenbaum auf. Somit hat man z.B. bei folgendem Code items.size() mal ein h:inputText im Komponentenbaum:

    <c:forEach items=”….”>
    <h:inputText …. />
    </c:forEach>

    ui:repeat jedoch arbeitet auf JSF-Komponenten-Ebene, d.h. es ist selber eine JSF-Komponente (UIRepeat) und somit auch selbst im Baum. Seine Kinder sind dabei nur einmal im Baum, werden aber mehrmals verarbeitet und gerendert (ähnlich wie auch bei h:dataTable). Zum Beispiel erzeugt folgender Code eine UIRepeat-Komponente mit einem h:inputText-Kind:

    <ui:repeat value=”…”>
    <h:inputText …/>
    </ui:repeat>

    Der Design-Bug lässt sich leicht erkennen. Zwei vermeintlich gleiche Tags machen etwas komplett verschiedenes.

    In deinem Fall kommt das Problem daher, dass ui:include auch auf Facelets-Ebene arbeitet. D.h. wenn der Komponentenbaum aufgebaut wird, wird dort wo ui:include steht einfach die angegebene Seite behandelt und ihr Inhalt (!!) in den Komponentenbaum aufgenommen. ui:include selbst scheint jedoch nicht im Komponentenbaum auf.

    Deswegen kann es nur mit c:forEach funktionieren. Alles klar?

    Liebe Grüße
    Jakob

    1. Danke Jakob!
      Deine Erklärungen müssten jetzt nur noch ihren Weg in die JavaDoc der Facelets finden…
      Denn da ist insbesondere ui:repeat recht mager erklärt. Als Alternative zu c:forEach

      “Use this tag as an alternative to h:dataTable or c:forEach, especially when you are using the jsfc feature of Facelets. You can specify this component as the value of the jsfc attribute, like this: <div… jsfc=”ui:repeat” value=”#{contacts}” var=”contact”>…”

Leave a Reply to mmueller Cancel reply

Your email address will not be published. Required fields are marked *