JSF Liste im RequestScope

anti-nerd

Mitglied
Morgen,

folgende Annahme:

Ich lese eine Liste aus einer DB aus und kann an ihr Änderungen vornehmen. Sagen wir, ich kann in diesem Beispiel nur Elemente Löschen. Hinter jedem Listenelement ist eine Schaltfläche zum Entfernen. Es soll aber nicht für jedes Löschen ein DB-Aufruf erfolgen, sondern erst ganz am Ende, etwa wenn ein Bestätigungsknopf betätigt wird oder man die Seite wechselt. Hierzu muss ich die aktuelle Liste lokal vorhalten. Ich habe das so gelöst:

Eine index.xhtml:

HTML:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
   <h:head>
	<title>Beispiel</title>
   </h:head>
   <h:body>
	<h:form>
	   <h:dataTable value="#{index.list}" var="entry">
		<h:column>
		   #{entry}
		</h:column>
		<h:column>
		   <h:commandButton actionListener="#{index.delete}" value="Entfernen">
			<f:attribute name="selected" value="#{entry}" />
		   </h:commandButton>
		</h:column>
	   </h:dataTable>
	</h:form>
   </h:body>
</html>

Ein BackingBean Index:

Java:
package backing;

import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.context.Flash;
import javax.faces.event.ActionEvent;

public class Index 
{
   private List list;
   private Flash flash;
   private String selected;

   @PostConstruct
   private void postConstruct ()
   {
	if(flash.get("list") == null)
	{
	   ArrayList arrayList = new ArrayList();
	   arrayList.add("Element 1");
	   arrayList.add("Element 2");
	   arrayList.add("Element 3");
	   flash.putNow("list", arrayList);
	   list = arrayList;
	}
	else
	{
	   flash.keep("list");
	   list = (List) flash.get("list");
	}
   }
   
   public void delete (ActionEvent actionEvent)
   {
	selected = (String) actionEvent.getComponent().getAttributes().get("selected");
	list.remove(selected);
   }
   
   // außerdem noch die üblichen Getter und Setter, die ich aus Übersichtsgründen mal unterschlage
    
}
[/Java]

Hier noch der Eintrag in der faces-config.xml:

[XML]
 <managed-bean>
	    <managed-bean-name>index</managed-bean-name>
	    <managed-bean-class>backing.Index</managed-bean-class>
	    <managed-bean-scope>request</managed-bean-scope>
	    <managed-property>
		 <property-name>flash</property-name>
		 <value>#{flash}</value>
	    </managed-property>
	 </managed-bean>
[/XML]

Ich hab jetzt schon alles mögliche getestet und rumprobiert, aber es hilft alles nichts, nach der ersten Aktion werden die Einträge nicht mehr richtig gelöscht. Entweder es passiert nichts, Ein Eintrag der beim letzten Mal gelöscht wurde wird gegen den getauscht, den man dieses Mal löschen will, man kann es auch hinbekommen, dass ein bereits gelöschter wieder auftaucht! Es steckt irgendeine Logik dahinter, aber ich habe keine Lust das hier jetzt alles darzustellen. Wenn man das Bean langlebiger deklariert, Session- oder ViewScoped tritt das Problem nicht auf.

Ich glaube, dass es etwas mit dem Zeitpunkt der Beanerzeugung zu tun hat - die aktuelle Liste liegt noch nicht vor, wenn sie in der postConstruct-Methode geladen wird...

Kann mir jemand weiterhelfen?
 
Zuletzt bearbeitet von einem Moderator:

mjustin

Aktives Mitglied
Flash wurde mit JSF 2.0 eingeführt, daher ist die faces-config.xml eigentlich nicht erfoderlich. Und dass in der faces-config.xml eine managed property namens flash definiert wird ist mir nicht klar. Ich denke, diese ist nicht erforderlich.

In GlassFish 3.0 erhalte ich beim Test der Anwendung diese Fehlermeldung:

Code:
Eigenschaft flash für verwalteten Bean "index ist nicht vorhanden

Das Flash Objekt wird im Programm nicht erzeugt. Es fehlt eine Initialisierung wie zum Beispiel

Code:
private Flash flash = (FacesContext.getCurrentInstance().getExternalContext().getFlash());
 
Zuletzt bearbeitet:

JimPanse

Bekanntes Mitglied
Ich habe noch nicht viel mit JSF 2.0 gemacht aber der FlashScope ist doch dafür gedacht - Daten über einen redirect d.h. eine Navigation zu einer nächsten Seite kurzfristig zwischen zu halten. Du navigierst aber nicht sondern machst ein 'forward' auf die gleiche Seite d.h. ist der FlashScope für diesen Use-Case unsinn...
 

mjustin

Aktives Mitglied
Hier ist das Problem nachvollziehbar. (GlassFish v3). Eine Recherche im Internet fand bisher kein Beispiel, in dem Flash in dieser etwas unkonventionellen Weise genutzt wird. Zumal es mit dem ViewScope ja auch problemlos funktioniert, ohne unnötig viele Serverresourcen zu belegen.
 
Zuletzt bearbeitet:

anti-nerd

Mitglied
Danke cerstmal für die Antworten!

Flash wurde mit JSF 2.0 eingeführt, daher ist die faces-config.xml eigentlich nicht erfoderlich. Und dass in der faces-config.xml eine managed property namens flash definiert wird ist mir nicht klar. Ich denke, diese ist nicht erforderlich.

Hmmn, das ganze ist ein Lernbeispiel wobei Ich mich an Auszügen aus JSF - the complete Reference orientiert habe. Auch wenn die faces-config.xml in JSF 2.0 umgangen werden kann, bleibt es eine generelle Frage der Konfigurierbarkeit. Alle wichtigen Einstellung der verwalteten Objekte in einem zentralen File zu haben und seinen Java-Code frei von Annotationen zu halten ist in meinen Augen auf lange Sicht sehr sinnvoll, weshalb ich es von Anfang an einsetzen wollte.

An anderen Stellen hab ich schon gesehen, dass die XML-Konfigurationsgeschichte Anhänger und Gegner hat und ich wollte jetzt keine Diskussion starten, nur erklären, weshalb ich das absichtlich so versucht habe.

Offensichtlich hat aber genau die Injektion des Flashs in das Bean als managed-property aus der faces-config nicht funktioniert! Dabei ist die Zeile auch dem Buch entnommen. Vielleicht habe ich mich versehen :autsch: Werde das gleich morgen überprüfen!

Das Flash Objekt wird im Programm nicht erzeugt. Es fehlt eine Initialisierung wie zum Beispiel
Code:
private Flash flash = (FacesContext.getCurrentInstance().getExternalContext().getFlash());

Damit funktioniert es dann auch wie gewünscht! :)

Was mich jetzt noch beschäftigt ist, wie man das deklarativ in XML-Form hinbekommt...

Ich habe noch nicht viel mit JSF 2.0 gemacht aber der FlashScope ist doch dafür gedacht - Daten über einen redirect d.h. eine Navigation zu einer nächsten Seite kurzfristig zwischen zu halten. Du navigierst aber nicht sondern machst ein 'forward' auf die gleiche Seite d.h. ist der FlashScope für diesen Use-Case unsinn...

Tut mir Leid, das hab ich einfach nur vergessen.

Hier die Änderungen, damit es läuft (mit redirect)

index.xhtml:
HTML:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
   <h:head>
	<title>Beispiel</title>
   </h:head>
   <h:body>
	<h:form>
	   <h:dataTable value="#{index.list}" var="entry">
		<h:column>
		   #{entry}
		</h:column>
		<h:column>
		   <h:commandButton action="index.xhtml?faces-redirect=true" actionListener="#{index.delete}" value="Entfernen">
			<f:attribute name="selected" value="#{entry}" />
		   </h:commandButton>
		</h:column>
	   </h:dataTable>
	</h:form>
   </h:body>
</html>

faces-config.xml:
[XML]
<managed-bean>
<managed-bean-name>index</managed-bean-name>
<managed-bean-class>backing.Index</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
[/XML]

Index.java:
Java:
package backing;
 
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.context.Flash;
import javax.faces.event.ActionEvent;
 
public class Index 
{
   private List list;
   private Flash flash = FacesContext.getCurrentInstance().getExternalContext.getFlash();
   private String selected;
 
   @PostConstruct
   private void postConstruct ()
   {
    if(flash.get("list") == null)
    {
       ArrayList arrayList = new ArrayList();
       arrayList.add("Element 1");
       arrayList.add("Element 2");
       arrayList.add("Element 3");
       flash.putNow("list", arrayList);
       list = arrayList;
    }
    else
    {
       flash.keep("list");
       list = (List) flash.get("list");
    }
   }
   
   public void delete (ActionEvent actionEvent)
   {
    selected = (String) actionEvent.getComponent().getAttributes().get("selected");
    list.remove(selected);
   }
   
   // außerdem noch die üblichen Getter und Setter, die ich aus Übersichtsgründen mal unterschlage
    
}

Noch mal die Frage: Weiß jemand wie man den Flash aus der faces.config.xhtml zugänglich macht?

EDIT: Ba! Ist es möglich innerhalb von Code- und HTML-Blöcken fett zu schreiben? Tut mir Leid, dass es so unübersichtlich ist...
 
Zuletzt bearbeitet:

anti-nerd

Mitglied
Problem gelöst!

Es fehlte die Angabe <property-class>, ohne diese Angabe wird defaultmäßig von String ausgegangen...

So ist es korrekt:

[XML]
<managed-bean>
<managed-bean-name>index</managed-bean-name>
<managed-bean-class>backing.Index</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>flash</property-name>
<property-class>javax.faces.context.Flash</property-class>
<value>#{flash}</value>
</managed-property>
</managed-bean>
[/XML]

Somit kann man sich das programmatische Holen der Referenz auf den Flash sparen. Falls das jemand ausprobiert - Man braucht die Getter und Setter, da der Container die setFlash-methode aufruft, wenn er das Bean erzeugt, um so die Referenz zum Flash zu übergeben!
 

Ähnliche Java Themen

Neue Themen


Oben