JSF Converter selectoneitem

hyperion

Bekanntes Mitglied
Hallo,

habe nochmal ein Problem mit dem Converter.

Ich habe eine Seite die mir ein Dropdown mit Werten aus der DB anzeigt. wähle ich eins aus und klicke auf Speichern, bekomme ich einen Fehler:
Code:
j_idt7:selectmenu: Überprüfungsfehler: Wert ist ungültig.

EinnahmeAnlegen.xhtml
Java:
<h:form>
		...
		<h:selectOneMenu id="selectmenu" value ="#{einnahmeAnlegenController.selectedEinnahmeArt}">  
			<f:converter   converterId="converterEinnahmeArt"/>                 
     		<f:selectItems var="#{einnahmeArt}" value="#{einnahmeAnlegenController.einnahmeArten}"
     					   itemLabel="#{einnahmeArt.text}" itemValue="#{einnahmeArt.einnahmeArtID}"/>
     	</h:selectOneMenu>
     	<h:commandButton value="Speichern" action="#{einnahmeAnlegenController.speichern}"/>
...
</h:form>

Java:
public class ConverterEinnahmeArt implements Converter {
	
    public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
//holt anhand des Strings das Objekt aus der DB
        return EinnahmeArt.getByID(Integer.parseInt(arg2.substring(0, arg2.indexOf(":"))));
    }
 
    public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
//Macht aus dem Objekt einen String        
        return arg2.toString();
    }
}

Leider klappt noch nicht einmal die Aufteilung value und label. Der generierte HTML-Code:
[XML]
...
<select id="j_idt7:selectmenu" name="j_idt7:selectmenu" size="1"> <option value="1:Gehalt" selected="selected">1:Gehalt</option>
<option value="2:Gehaltsbonus">2:Gehaltsbonus</option>
<option value="3:Zinsen">3:Zinsen</option>
<option value="4:Bank Of Scotland Zinsen">4:Bank Of Scotland Zinsen</option>
<option value="5:Kindergeld">5:Kindergeld</option>
</select></td>
</tr>
<tr>
<td><input type="submit" name="j_idt7:j_idt13" value="Speichern" />
...
[/XML]

Ansich würde ich mir wünschen, dass bei value nur die 1,2,..,5 steht und als Text nur Gehalt etc.
Die Form ID:Text kommt von der toString() Methode:

Java:
public class EinnahmeArt{
...
public String toString(){
		return einnahmeArtID + ":" + text;
	}
...
}

Wäre nett, wenn mir wieder jemand weiterhelfen könnte.

Gruß hyperion
 

sence

Bekanntes Mitglied
Hallo,

1)
der "var" type bei selectItem ist: java.lang.String
ändere mal
<f:selectItems var="#{einnahmeArt}"
auf:
<f:selectItems var="einnahmeArt"

selectItems (JSF 2.0 Page Decraration Language: Facelets Variant)

2)
du verwendest mindestens Viewscope in deiner Bean ?

3)
value ="#{einnahmeAnlegenController.selectedEinnahmeArt}">
selectedEinnahmeArt muss vom type int / Long sein.

4)
Solltest du nur die ID als Wert abspeichern brauchst du (sofern ich nicht gerade schlafe)
keinen Konverter.

erst wenn du ein Object speichern möchtest:
Java:
<f:selectItems value="#{bean.lst_srcObjects}" var="object" itemLabel="#{object.name}" itemValue="#{object}"/>

grüße
 
Zuletzt bearbeitet:

hyperion

Bekanntes Mitglied
Hallo,

Danke für die Antwort.

1 und 2 habe ich nun geändert.

3)
value ="#{einnahmeAnlegenController.selectedEinnahmeArt}">
selectedEinnahmeArt muss vom type int / Long sein.
selectOneMenu (JSF 2.0 Page Decraration Language: Facelets Variant)
Hier steht, dass es ein Object sein muss.

4)
Solltest du nur die ID als Wert abspeichern brauchst du (sofern ich nicht gerade schlafe)
keinen Konverter.
Also ich würde das schon ganz gern mit Converter machen. Ich möchte das ja kennenlernen.

Ansich ist doch das so gedacht. Ich gebe der DropDown eine Liste von EinnahmeArt Objekten und einen Converter für die Objekte mit. Der Converter macht aus jedem dieser Objekte einen String(In meinem Fall ID:Text). Wähle ich in der DropDown etwas aus und klicke auf Speichern, macht der Converter wieder aus dem String ein EinnahmeArt Objekt und das steht dann in selectedEinnahmeArt. So verstehe ich das. Daraum verstehe ich auch nicht warum das ein int oder long sein soll.

Nach den Änderungen 1 und 2 erhalte ich leider immer noch den Quelltext:
[XML]
<option value="2:Gehaltsbonus">2:Gehaltsbonus</option>
<option value="3:Zinsen">3:Zinsen</option>
<option value="4:Bank Of Scotland Zinsen">4:Bank Of Scotland Zinsen</option>
<option value="5:Kindergeld">5:Kindergeld</option>
[/XML]

hat denn
[XML]
<f:selectItems var="einnahmeArt" value="#{einnahmeAnlegenController.einnahmeArten}"
itemLabel="#{einnahmeArt.text}" itemValue="#{einnahmeArt}"/>
[/XML]
gar keinen Einfluss? Ich gebe doch extra itemLabel und itemValue an.

Gruß hyperion
 

sence

Bekanntes Mitglied
Du hast deklariert:
Java:
itemValue="#{einnahmeArt.einnahmeArtID}"

Daher gehe ich einfach mal (abgeleitet von dem Begriff "ID") von einer Zahl aus, die als Wert (=value)
gespeichert werden soll.
-> deshalb meinte ich, du kannst dann gleich ein int/long angeben.

würdest du schreiben:
Java:
itemValue="#{einnahmeArt}"

würdest du das ganze Object als Wert speichern.
(dann wäre Object als Datentyp richtig, oder auch entsprechend ein Objekt der Klasse EinnahmeArt)

Dann müsste dein Konverter greifen

JSFAtWork, JSF 2.0 und Apache MyFaces
 

hyperion

Bekanntes Mitglied
Hallo,

leider funktioniert es so auch nicht.
Ich versuche nochmal die aktuelle Situation zu posten diesmal auch mit den eher unwichtigen Teilen. Vielleicht ist dort ja der Fehler. Ich denke es liegt immernoch irgendwie an dem Converter, da Speichern eigentlich EinnahmeAnlegenController.speichern() aufruft und diese Methode nur "fehler" zurückgibt. Man sollte also eigentlich bei fehler.jsf rauskommen allerdings bleibe ich immer auf EinnahmeAnlegen.jsf. Es scheitert also schon vor dem Methodenaufruf.

EinnahmeAnlegn.xhtml
[XML]
<!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"
xmlns:ui="http://java.sun.com/jsf/facelets">

<h:head></h:head>
<body>
<h:messages style="color:red;margin:8px;" />
<h:form>
<h:panelGrid columns="2">
<h:eek:utputText value="Betrag:" />
<h:inputText id="betrag" value="#{einnahmeAnlegenController.betrag}" required="true"/>

<h:eek:utputText value="Datum:" />
<h:inputText id="datum" required="true" value="#{einnahmeAnlegenController.datum}" >
<f:convertDateTime pattern="dd.MM.yyyy"/>
</h:inputText>

<h:eek:utputText value="Art:" />
<h:selectOneMenu id="selectmenu" value ="#{einnahmeAnlegenController.selectedEinnahmeArt}">
<f:converter converterId="converterEinnahmeArt"/>
<f:selectItems var="einnahmeArt" value="#{einnahmeAnlegenController.einnahmeArten}"
itemLabel="#{einnahmeArt.text}" itemValue="#{einnahmeArt}"/>
</h:selectOneMenu>
<h:commandButton value="Speichern" action="#{einnahmeAnlegenController.speichern}"/> </h:panelGrid>
</h:form>
</body>
</html>
[/XML]

erzeugt folgendes HTML:

[XML]
<!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"><head></head>
<body><ul style="color:red;margin:8px;"><li> j_idt7:selectmenu: Überprüfungsfehler: Wert ist ungültig. </li></ul>
<form id="j_idt7" name="j_idt7" method="post" action="/MyHome/EinnahmeAnlegen.jsf" enctype="application/x-www-form-urlencoded">
<input type="hidden" name="j_idt7" value="j_idt7" />
<table>
<tbody>
<tr>
<td>Betrag:</td>
<td><input id="j_idt7:betrag" type="text" name="j_idt7:betrag" value="10.0" /></td>
</tr>
<tr>
<td>Datum:</td>
<td><input id="j_idt7:datum" type="text" name="j_idt7:datum" value="01.01.2012" /></td>
</tr>
<tr>
<td>Art:</td>
<td><select id="j_idt7:selectmenu" name="j_idt7:selectmenu" size="1"> <option value="1:Gehalt" selected="selected">1:Gehalt</option>
<option value="2:Gehaltsbonus">2:Gehaltsbonus</option>
<option value="3:Zinsen">3:Zinsen</option>
<option value="4:Bank Of Scotland Zinsen">4:Bank Of Scotland Zinsen</option>
<option value="5:Kindergeld">5:Kindergeld</option>
</select></td>
</tr>
<tr>
<td><input type="submit" name="j_idt7:j_idt13" value="Speichern" /></td>
</tr>
</tbody>
</table>
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAA...AAA" autocomplete="off" />
</form>
</body>
</html>
[/XML]

ConverterEinnahmeArt
Java:
public class ConverterEinnahmeArt implements Converter {

	
    public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
        return EinnahmeArt.getByID(Integer.parseInt(arg2.substring(0, arg2.indexOf(":"))));
    }
 
    public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {        
        return arg2.toString();
    }
}

EinnahmeAnlegenController
Java:
@ManagedBean
@ViewScoped
public class EinnahmeAnlegenController implements Serializable{

	private static final long serialVersionUID = 1L;
	private double betrag;
	private Date datum;
	private EinnahmeArt selectedEinnahmeArt;

	//... getter und setter zu allen Attributen
	
	public String speichern(){	
		return "fehler"; // Zum Debuggen
		/*		Einnahme einnahme = new Einnahme();
		Meldung meldung = (Meldung)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("meldung");
		int betragInCent = (int)betrag*100;
		einnahme.setBetragInCent(betragInCent);
		einnahme.setDatum(datum);
		einnahme.setEinnahmeArtID(selectedEinnahmeArt.getEinnahmeArtID());
		try {
			einnahme.insert();
		} catch (ClassNotFoundException | SQLException e) {
			//			meldung.setMeldung(e.getMessage());
			return "EinnahmeAnlegen";
		}
				meldung.setMeldung("Erfolg");
		return "index";*/
	}

	public List<SelectItem> getEinnahmeArten(){	
		try {
			return EinnahmeArt.getEinnahmeArten();
		} catch (SQLException e) {
			List<SelectItem> a = new ArrayList<SelectItem>();
			a.add(new SelectItem("test SQL"));			
			return a;
		} catch(ClassNotFoundException e){			
			List<SelectItem> a = new ArrayList<SelectItem>();
			a.add(new SelectItem("test Class"));			
			return a;
		}
	}
}

EinnahmeArt
Java:
public class EinnahmeArt {
	
	private int einnahmeArtID;
	private int vaterEinnahmeArtID;
	private String text;	

	//...setter und getter zu allen Attributen
	
	public static List<SelectItem> getEinnahmeArten() throws ClassNotFoundException, SQLException{
		Statement st = null;
		ResultSet rs = null;
		
		List<SelectItem> einnahmeArten = new ArrayList<SelectItem>();
			st = DBUtil.getStatement();
			rs = st.executeQuery("SELECT \"EinnahmeArtID\", \"Text\" FROM \"EinnahmeArt\";");
			
			while (rs.next()) {
				EinnahmeArt einnahmeArt = new EinnahmeArt();
				einnahmeArt.setEinnahmeArtID(rs.getInt("EinnahmeArtID"));
				einnahmeArt.setText(rs.getString("Text"));
	            einnahmeArten.add(new SelectItem(einnahmeArt));
	        }		
			rs.close();
			DBUtil.closeStatement(st);
			return einnahmeArten;		
	}
	
	public static EinnahmeArt getByID(int id){
		ResultSet rs = null;
		EinnahmeArt einnahmeArt = null;
		try{
		Statement st = DBUtil.getStatement();
			String query = "SELECT \"EinnahmeArtID\", \"Text\", \"VaterEinnahmeArtID\" " +
					 "FROM \"EinnahmeArt\" WHERE \"EinnahmeArtID\" = '" + id + "';";
			rs = st.executeQuery(query);			
			while (rs.next()) {
				einnahmeArt = new EinnahmeArt();
				einnahmeArt.setEinnahmeArtID(rs.getInt("EinnahmeArtID"));
				einnahmeArt.setText(rs.getString("Text"));
				einnahmeArt.setVaterEinnahmeArtID(rs.getInt("VaterEinnahmeArtID"));            
	        }
			rs.close();
		
			DBUtil.closeStatement(st);
		}catch(Exception e){
			e.printStackTrace();
		}
		return einnahmeArt;
	}
	
	public String toString(){
		return einnahmeArtID + ":" + text;
	}	
}

Einnahme
Java:
public class Einnahme {

	int einnahmeID;
	int betragInCent;
	Date datum;
	int einnahmeArtID;
	
	//...getter und setter für alle Attribute
	
	public void insert() throws ClassNotFoundException, SQLException{
		Statement st = null;
		st = DBUtil.getStatement();	
		String query = "INSERT INTO \"Einnahme\" (\"BetragInCent\", \"Datum\", \"EinnahmeArtID\") " +
				"VALUES ('" + betragInCent + "', '" + datum + "', '" + einnahmeArtID + "');";
	    st.executeUpdate(query);
		DBUtil.closeStatement(st);
	}
}

Gruß hyperion
 

SSAT

Mitglied
Implementiere in der Klasse EinnahmeArt die equals-Methode.
Wenn ich micht richtig erinnere, führt JSF nach dem Aufruf von getAsObject() im Converter noch einen Vergleich mit den internen Modell des selectOneMenu durch. Da du bei EinnahmeArt#getByID ein neues Objekt erzeugst, ergibt der Referenzvergleich false und jsf sagt dir dann, dass der Wert (dein neues EinnahmeArt-Objekt) ungültig ist, weil es eben nicht im internen Modell vorhanden ist.
 

hyperion

Bekanntes Mitglied
Das war die Lösung.

Habe noch folgendes in EinnahmeArt hinzugefügt:
Java:
public boolean equals(Object o){
		if(o instanceof EinnahmeArt) {
		    if(this.text.equals(((EinnahmeArt)o).text) &&
		       this.einnahmeArtID == ((EinnahmeArt)o).einnahmeArtID){
		    	   return true;
		       }else{
		    	   return false;
		       }
		    
		}else{
			return false;
		}
	}

Gruß hyperion
 

sence

Bekanntes Mitglied
Kleine anmerkung:
wenn du Zeit und Lust hast, schau dir mal Hibernate an, vereinfacht vieles in Sachen SQL

wieso speicherst du die Message in der Session ab, hat es nen speziellen Grund?
du verwendest:

Java:
<h:messages showDetail="true"/>

in der View, aber ich sehe kein

Java:
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,"Das ist eine Nachricht", "erfolgreich"));

gibst du selbst die Meldung aus, oder wirfst du Sie an anderer Stelle (die FacesMessage)

grüße
 

hyperion

Bekanntes Mitglied
Hallo,

ich habe schonmal vor ein zwei Jahren versucht mich mit JEE zu beschäftigen. Ich habe damals dann versucht alle möglichen Frameworks gleichzeitig zu verwenden. Irgendwie hat das aber nie wirklich geklappt. Ich möchte jetzt erstmal nur mit JSF anfangen. Dann Hibernate dazunehmen und dann Spring. Achja und irgendwann zwischenrein mal JUnit.

wieso speicherst du die Message in der Session ab, hat es nen speziellen Grund?
du verwendest:

Java Code: Quelltext in neuem Fenster öffnen

1



<h:messages showDetail="true"/>


in der View, aber ich sehe kein

Java Code: Quelltext in neuem Fenster öffnen

1



FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,"Das ist eine Nachricht", "erfolgreich"));


gibst du selbst die Meldung aus, oder wirfst du Sie an anderer Stelle (die FacesMessage)

Nein, da hast du mich erwischt:) Ich habe nicht viel Ahnung.
Ich dachte eigentlich, dass ich mit
<h:messages style="color:red;margin:8px;" />
alle Fehlermeldungen ausgebe, die so bei JSF auftreten. Ich hatte angenommen, dass ich dafür nichts weiter machen muss.

mit
<h:eek:utputText value="#{meldung.meldung}" />
wollte ich eignetlich alle Arten von Meldungen ausgeben. Kunde erfolgreich angelegt, etc. Warum das SessionScoped ist. Öhm naja am Anfang habe ich noch nichts angegeben und da hat es noch nicht funktioniert. Zum Testen hab ich dann mal @SessionScoped gemacht und dann hats geklappt(dann bliebs halt so). ViewScoped sollte reichen oder?

Danke für die Tipps. Ich freue mich immer, wenn jemanden so etwas auffällt und es dann kurz schreibt.

Gruß hyperion
 

sence

Bekanntes Mitglied
Java:
<h:messages style="color:red;margin:8px;" />
hiermit werden alle Meldungen ausgegeben:
INFO, WARN,ERROR,FATAL

Du kannst selbst mittels
Java:
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO,"Das ist eine Nachricht", "erfolgreich"));
Meldungen "feuern".

Mit: FacesMessage.SEVERITY_INFO
kannst du die Art der Meldung bestimmen, also ob info, warn usw (siehe oben)

Diese werden automatisch beim zurückkehren auf die View ausgegeben.
(somit musst du diese nicht in der Session ablegen und später wieder herausholen, ledeglich <h:messages showDetail="true"/> einbinden)

ViewScoped)
solange der Benutzer auf der selben Seite verbleibt, sind alle Werte der Bean Propertys vorhanden.
Ruft der Benutzer die Seite neu auf, wird deine Bean neu instanziert und alle voherigen "zustände" der Bean Propertys sind verloren.

(wird für kurze konversationen verwendet)

SessionScopes)
Solange die Session aktiv ist, bleiben die Werte der Bean Property erhalten, sowie die Bean selbst.


Grüße
 

Ähnliche Java Themen

Neue Themen


Oben