myFaces - SelectItem

Status
Nicht offen für weitere Antworten.

y0dA

Top Contributor
Hi!
Wie handhabt ihr SelectItems? Sprich wenn ihr ein Dropdown in der View habt, welches mit SelectItems dargestellt wird, was setzt ihr als value?

Code:
new SelectItem(Object value, String label)

Mein Problem:
Wenn ein Umlaut in meiner List von SelectItems als value gesetzt wird dann kann ich im Dropdown so oft wie ich möchte diese Zeile auswählen - sie wird nicht angenommen! Also im label macht ein Umlaut keine Probleme aber im value.

mfg
 
M

maki

Gast
Wieso sollte ein Value einen Umlaut enthalten?

Value ist ein Objekt, nicht nur ein doofer String.

Nachtzrag: Klar sind Strings auch Objekte, aber ich hoffe du verstehst worauf ich hinaus will.
 

y0dA

Top Contributor
maki hat gesagt.:
Wieso sollte ein Value einen Umlaut enthalten?

Value ist ein Objekt, nicht nur ein doofer String.

Nachtzrag: Klar sind Strings auch Objekte, aber ich hoffe du verstehst worauf ich hinaus will.

Ja und mein Problem ist wenn value ein String ist, dass ich dann im Dropdown diesen Wert zwar setzen kann, beim nächsten submit oder refresh der Seite, der Wert aber nicht im Controller dieser jsp gesetzt wird. Sprich wenn der Wert kein Umlaut beinhaltet dann wird er gesetzt andernfalls wird die setter methode im Controller nicht angesprochen..
 

y0dA

Top Contributor
Wenn ich für value ein eigenes Objekt erstellt habe, welche bspw. ort, strasse,land und einen identifier als Klassenmember besitzt, muss ich hiefür dann einen eigenen Converter schreiben?

bsp:
Code:
for (PlanningPoint p : l) {
			geoStartDropDownList.add(new SelectItem(p, p.getIdentifier()));
		}
Hier befülle ich nun die Liste mit value = PlanningPoint Objekt und label eben ein Feld aus dieser Klasse.

Wenn ich das dann in der View wie folgt mache, bekomme ich (wohl klarerweise) eine Exception:
Code:
<h:selectOneMenu id="planGeoDropdown1"
				value="#{planningContrl.geoStartDropDownSelected}"
				styleClass="outputText"
				rendered="#{planningContrl.renderGeoDropDownStart}">
				<f:selectItems value="#{planningContrl.geoStartDropDownList}" />
			</h:selectOneMenu>


**EDIT**
Ich formuliere die Frage um, macht es Sinn komplexe Typen im SelectItem als value anzugeben? Oder sollte man die Objekte bspw in einer Map halten und den key der Map als value des SelectItems setzen?
 
M

maki

Gast
Ich formuliere die Frage um, macht es Sinn komplexe Typen im SelectItem als value anzugeben? O
Absolutes ja!

Oder sollte man die Objekte bspw in einer Map halten und den key der Map als value des SelectItems setzen?
Nein!

Natürlich nur, wenn man die Möglichkeit hat, wenn die App schon Transfer Objekte braucht (zB weil man ohne Domain/Business Model arbeitet), hat man eben nur TOs.
 

y0dA

Top Contributor
maki hat gesagt.:
Ich formuliere die Frage um, macht es Sinn komplexe Typen im SelectItem als value anzugeben? O
Absolutes ja!

Oder sollte man die Objekte bspw in einer Map halten und den key der Map als value des SelectItems setzen?
Nein!

Natürlich nur, wenn man die Möglichkeit hat, wenn die App schon Transfer Objekte braucht (zB weil man ohne Domain/Business Model arbeitet), hat man eben nur TOs.

Da ich bisher leider das Thema Converter komplett ignoriert habe, stellt sich mir die Frage wie ich einen Converter für so eine komplexe Klasse implementieren soll.

Code:
Object getAsObject(FacesContext, UIComponent, String)

Code:
String getAsString(FacesContext, UIComponent, Object)

Diese beiden Methoden gehören implementiert, nur finde ich kein Bsp welches mein Problem abdeckt, da bei den ganzen Bsp Convertern immer nur "simple" Typen konvertiert werden (String,boolean,enum).

Hast du vllt ein Bsp für mich?

Wie soll ich die Methode getAsString implementieren? Alle Klassenmember in einem String zurückliefern?
 
M

maki

Gast
getAsString soll einen String zurückliefern, den du brauchst um das Objekt wieder herzustellen bzw. zu "converten".

Wenn die Entities mit einer Id hast, könnte man das hernehmen, dann mit der Id zB aus einer HashMap oder der DB das Objekt wiederholen, wobei ersteres performanter wäre.

Jaja ioch weiss, mein "Nein!" zu deiner Frage war übereilt ;)
 

y0dA

Top Contributor
Aber ich könnte auch alle Klassenmember Werte in einen String packen und bspw mit "," aneinanderstöpseln und bei der Methode getAsObject den umgekehrten Weg verfolgen?

Möchte nicht unbedingt mittels einer id und einer map das ganze lösen - dann könnte ich mir nämlich auch den konverter sparen!

passt das so (meine Gedankengänge^^).
 
M

maki

Gast
Solltest dich mehr mit Convertern beafssen, ganz wichtig in JSF.
 

y0dA

Top Contributor
Bin ja grad dabei!
Weiters sauge ich ja auch dein Wissen, obwohl du damit grade wieder geizt!
 
M

maki

Gast
Tut mir leid wenn dass dein Eindruck ist.

Wie gesagt, getAsString gibt dir einen String, diesen bekommst du zurück, und must daraus dein Objekt machen.
Den String von getAsString solltest du nicht anzeigen, wird intern vom Converter verwendet, ist zB der "value" eines SelectItems, während nur das "label" angezeigt wird.

Möchte dir die üblichen "TelephoneNumber" Konverter Beispiele ersparen, gibnt aber auch genug Beispiele die es mit Enums zeigen.

Jedenfalls musst du genug infos in den String packen um daraus wieder das Objekt zu machen.

Code aus meinem aktuellen Projekt wird dir nix nutzen, weil noch ein riesen Framework aussenrum ist, das ich erstens selbst nicht ganz verstehe und zweitens auch nicht als empfehlenswert betrachte (manchmal würde ich Mitarbeiter ganz gerne zwingen opensource zu verwenden bevor man sein eigenes Süppchen kocht).
 

y0dA

Top Contributor
Na, war nur ein Scherz, da deine vorhergehende Antwort eine Frage nicht beantwortet hatte :D

Ok, ich hab mir nun einen Konverter erstellt:
Code:
public class PlanningPointConverter implements Converter {

	//------------------------------------------------------------------------------------------------------------------
	// HELPER METHODS
	//------------------------------------------------------------------------------------------------------------------

	/**
	 * interface implementation
	 * @param context context
	 * @param component component
	 * @param value value
	 * @return <code>{@link #PlanningPoint</code> object
	 */
	public Object getAsObject(final FacesContext context, final  UIComponent component, final String value)
			throws ConverterException {
		if (value.isEmpty()) {
			return null;
		}
		PlanningPoint p = new PlanningPoint();
	    String [] pComps = StringUtility.split(value, " ");
	    p.setCountry(pComps[0]);
	    p.setLocation(pComps[1]);
	    p.setStreet(pComps[2]);
	    p.setPostCode(pComps[3]);
	    p.setHouseNumber(pComps[4]);
	    p.setIdentifier(pComps[5]);
	    return p;
	}	

	/**
	 * interface implementation
	 * @param context context
	 * @param component component
	 * @param value value
	 * @return <code>{@link #PlanningPoint</code> as string
	 */
	public String getAsString(final FacesContext context, final  UIComponent component, final Object value)
			throws ConverterException {
		if (value != null) {
			return ((PlanningPoint) value).toString();	
		} else {
			return null;
		}
	}
}

Nun der Konverter funktioniert auch, sprich die beiden Methoden liefern korrekte Werte. Mein Problem ist jetzt, dass ich diesen Konverter 2 mal innerhalb einer panelgrid benötige:
Code:
<h:panelGrid id="planningPG5" columns="2" border="1">
			<h:outputText value="#{labels.planStartDropdown}"
				styleClass="outputText"
				rendered="#{planningContrl.renderGeoDropDownStart}" />
			<h:selectOneMenu id="planGeoDropdown1"
				value="#{planningContrl.geoStartDropDownSelected}"
				converter="planningPointConverter" styleClass="outputText"
				rendered="#{planningContrl.renderGeoDropDownStart}">
				<f:selectItems value="#{planningContrl.geoStartDropDownList}" />
			</h:selectOneMenu>
			<h:outputText value="#{labels.planEndDropdown}"
				styleClass="outputText"
				rendered="#{planningContrl.renderGeoDropDownEnd}" />
			<h:selectOneMenu id="planGeoDropdown2"
				value="#{planningContrl.geoEndtDropDownSelected}"
				converter="planningPointConverter" styleClass="outputText"
				rendered="#{planningContrl.renderGeoDropDownEnd}">
				<f:selectItems value="#{planningContrl.geoEndDropDownList}" />
			</h:selectOneMenu>
		</h:panelGrid>

Und nun verhält sich das ganze so, dass wenn ich von einem der beiden DropDowns die Werte "hole" - mittels einen cmd button, dann reagiert meine Applikation nicht mehr, wenn ich beim anderen DropDown das gleiche machen möchte - es wird keine Methoden aufgerufen..
 

y0dA

Top Contributor
Nein ich bekomme überhaupt keine Fehler - aber alle meine Buttons führen ihre Aktionen nicht mehr aus...

**EDIT**
gut, facetrace meint bei der Validation gibt es einen error:

Code:
subViewPlanning:fPlanning:planGeoDropdown1: Validierungsfehler: Wert ist keine gültige Auswahl
 
M

maki

Gast
Was heisst schon Fehler? Gilt das auch für Messages?

Musst schon genauer sein.

Benutzt du Facestrace oder nicht?
 

y0dA

Top Contributor
ja ich benutze Facestrace und u.a. wird folgendes bei Facestrace ausgegeben:
Code:
ERROR MESSAGES
Component ID: subViewPlanning:fPlanning:planGeoDropdown1
Severity: ERROR
Message: subViewPlanning:fPlanning:planGeoDropdown1: Validierungsfehler: Wert ist keine gültige Auswahl

Und das ganze passiert erst wenn ich ein weiteres mal dasselbe dropdown befüllen möchte oder wenn ich das andere befüllen möchte - sprich beim 1. mal kann ich das dropdown befüllen ein weiteres mal geht es nicht.


Passt das überhaupt so:
Code:
<h:selectOneMenu id="planGeoDropdown1"
				value="#{planningContrl.geoStartDropDownSelected}"
				converter="planningPointConverter" styleClass="outputText"
				rendered="#{planningContrl.renderGeoDropDownStart}">
				<f:selectItems value="#{planningContrl.geoStartDropDownList}" />
			</h:selectOneMenu>
Sprich, gehört der converter so hinzugefügt - muss ich eventuell mit
Code:
<f:converter/>
arbeiten?
faces.config:
Code:
<converter>
		<converter-id>planningPointConverter</converter-id>
		<converter-class>
			at.pcd.wam.gegenstandsbereich.tmcWeb.common.PlanningPointConverter
		</converter-class>
	</converter>


**EDIT**
Gut wenn ich es so mache:
Code:
<h:selectOneMenu id="planGeoDropdown1"
				value="#{planningContrl.geoStartDropDownSelected}"
				rendered="#{planningContrl.renderGeoDropDownStart}">
				<f:converter converterId="planningPointConverter" />
				<f:selectItems value="#{planningContrl.geoStartDropDownList}" />
			</h:selectOneMenu>
Bekomme ich folgende Exception:
Code:
javax.faces.FacesException: Exception while calling encodeEnd on component : {Component-Path : [Class: javax.faces.component.UIViewRoot,ViewId: /jsp/secure/main.jsp][Class: org.apache.myfaces.custom.div.Div,Id: mainDiv3][Class: javax.faces.component.UINamingContainer,Id: subViewPlanning][Class: javax.faces.component.html.HtmlForm,Id: fPlanning][Class: javax.faces.component.html.HtmlPanelGrid,Id: planningPG5]}
	at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:559)
	at javax.faces.component.UIComponent.encodeAll(UIComponent.java:250)
	at javax.faces.component.UIComponent.encodeAll(UIComponent.java:246)
	at javax.faces.component.UIComponent.encodeAll(UIComponent.java:246)
	at javax.faces.component.UIComponent.encodeAll(UIComponent.java:246)
	at javax.faces.component.UIComponent.encodeAll(UIComponent.java:246)
	at org.apache.myfaces.application.jsp.JspViewHandlerImpl.actuallyRenderView(JspViewHandlerImpl.java:423)
	at org.apache.myfaces.application.jsp.JspViewHandlerImpl.renderView(JspViewHandlerImpl.java:380)
	at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:41)
	at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:140)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:152)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:147)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
	at java.lang.Thread.run(Thread.java:619)
Caused by: javax.faces.FacesException: Exception while calling encodeEnd on component : {Component-Path : [Class: javax.faces.component.UIViewRoot,ViewId: /jsp/secure/main.jsp][Class: org.apache.myfaces.custom.div.Div,Id: mainDiv3][Class: javax.faces.component.UINamingContainer,Id: subViewPlanning][Class: javax.faces.component.html.HtmlForm,Id: fPlanning][Class: javax.faces.component.html.HtmlPanelGrid,Id: planningPG5][Class: javax.faces.component.html.HtmlSelectOneMenu,Id: planGeoDropdown1]}
	at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:559)
	at org.apache.myfaces.shared_impl.renderkit.RendererUtils.renderChild(RendererUtils.java:515)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlGridRendererBase.renderChildren(HtmlGridRendererBase.java:221)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlGridRendererBase.encodeEnd(HtmlGridRendererBase.java:102)
	at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:556)
	... 25 more
Caused by: java.lang.NullPointerException
	at org.apache.myfaces.taglib.core.DelegateConverter.getAsString(DelegateConverter.java:101)
	at org.apache.myfaces.shared_impl.renderkit.RendererUtils.getConvertedStringValue(RendererUtils.java:621)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlRendererUtils.getSubmittedOrSelectedValuesAsSet(HtmlRendererUtils.java:362)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlRendererUtils.internalRenderSelect(HtmlRendererUtils.java:337)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlRendererUtils.renderMenu(HtmlRendererUtils.java:288)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlMenuRendererBase.encodeEnd(HtmlMenuRendererBase.java:57)
	at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:556)
	... 29 more
 
M

maki

Gast
Ist anscheinend doch keine so gute Idee null zurückzugeben wenn ein String bzw. Objekt erwartet wird ;)
 

y0dA

Top Contributor
Wie meinen?

Wäre dir sehr dankbar, wenn du dich näher darüber ausläßt - sitz ich hier schon 2h an dem Problem..


Was soll ich machen wenn der Converter null bekommt - ich kann mir ja kein Objekt herzaubern. Abgesehen davon geht er beim debuggen nicht in die Verzweigung wo null zurückgegeben wird.
 
M

maki

Gast
Caused by: java.lang.NullPointerException
at org.apache.myfaces.taglib.core.DelegateConverter.getAsString(DelegateConverter.java:101)
Anscheinend darf getAsString kein null zurückliefern.
 

y0dA

Top Contributor
maki hat gesagt.:
Caused by: java.lang.NullPointerException
at org.apache.myfaces.taglib.core.DelegateConverter.getAsString(DelegateConverter.java:101)
Anscheinend darf getAsString kein null zurückliefern.

Tut es auch nicht!
Ich habe dort einen Debugpoint gesetzt - da läuft er nicht rein, weiters diese Stelle mit "" geschmückt anstatt mit null --> keine Änderung des Problems.

Passt denn mein Converter?

Hier nochmal die Klassen:
Code:
public class PlanningPoint {

	//------------------------------------------------------------------------------------------------------------------
    // FIELDS
    //------------------------------------------------------------------------------------------------------------------
	
	/** country */
	private String country;
	
	/** location */
	private String location;
	
	/** street */
	private String street;
	
	/** post code */	
	private String postCode;
		
	/** house number */
	private String houseNumber;
	
	/** identifier */
	private String identifier;
	
	//------------------------------------------------------------------------------------------------------------------
    // CONSTRUCTOR
    //------------------------------------------------------------------------------------------------------------------
	
	/**
	 * constructor
	 * @param country country
	 * @param location location
	 * @param street street
	 * @param postCode postCode
	 * @param houseNumber houseNumber
	 */
	public PlanningPoint(final String country, final String location, final String street,
			final String postCode, final String houseNumber) {
		super();
		this.country = country;
		this.location = location;
		this.street = street;
		this.postCode = postCode;
		this.houseNumber = houseNumber;
	}

	/**
	 * empty constructor
	 */
	public PlanningPoint() {
		// nothing to do
	}
	
	//------------------------------------------------------------------------------------------------------------------
    // HELPER METHODS
    //------------------------------------------------------------------------------------------------------------------
	
	/**
	 * returns a string representation of the object
	 * @return object as string
	 */
	public String toString() {
		return this.country + ";" + this.location + ";" + this.street + ";" + this.postCode + ";"
			+ this.houseNumber + ";" + this.identifier;
	}
	
	//------------------------------------------------------------------------------------------------------------------
    // GETTER / SETTER
    //------------------------------------------------------------------------------------------------------------------

	/**
	 * getter method
	 * @return the country
	 */
	public String getCountry() {
		return this.country;
	}

	/**
	 * setter method
	 * @param country the country to set
	 */
	public void setCountry(final String country) {
		this.country = country;
	}

	/**
	 * getter method
	 * @return the location
	 */
	public String getLocation() {
		return this.location;
	}

	/**
	 * setter method
	 * @param location the location to set
	 */
	public void setLocation(final String location) {
		this.location = location;
	}

	/**
	 * getter method
	 * @return the street
	 */
	public String getStreet() {
		return this.street;
	}

	/**
	 * setter method
	 * @param street the street to set
	 */
	public void setStreet(final String street) {
		this.street = street;
	}

	/**
	 * getter method
	 * @return the postCode
	 */
	public String getPostCode() {
		return this.postCode;
	}

	/**
	 * setter method
	 * @param postCode the postCode to set
	 */
	public void setPostCode(final String postCode) {
		this.postCode = postCode;
	}

	/**
	 * getter method
	 * @return the houseNumber
	 */
	public String getHouseNumber() {
		return this.houseNumber;
	}

	/**
	 * setter method
	 * @param houseNumber the houseNumber to set
	 */
	public void setHouseNumber(final String houseNumber) {
		this.houseNumber = houseNumber;
	}

	/**
	 * getter method
	 * @return the identifier
	 */
	public String getIdentifier() {
		return this.identifier;
	}

	/**
	 * setter method
	 * @param identifier the identifier to set
	 */
	public void setIdentifier(final String identifier) {
		this.identifier = identifier;
	}
}

Code:
public class PlanningPointConverter implements Converter {

	//------------------------------------------------------------------------------------------------------------------
	// HELPER METHODS
	//------------------------------------------------------------------------------------------------------------------

	/**
	 * interface implementation
	 * @param context context
	 * @param component component
	 * @param value value
	 * @return <code>{@link #PlanningPoint</code> object
	 */
	public Object getAsObject(final FacesContext context, final  UIComponent component, final String value)
			throws ConverterException {
		if (value.isEmpty()) {
			return null;
		}
		PlanningPoint p = new PlanningPoint();
	    String [] pComps = StringUtility.split(value, ";");
	    p.setCountry(pComps[0]);
	    p.setLocation(pComps[1]);
	    p.setStreet(pComps[2]);
	    p.setPostCode(pComps[3]);
	    p.setHouseNumber(pComps[4]);
	    p.setIdentifier(pComps[5]);
	    return p;
	}	

	/**
	 * interface implementation
	 * @param context context
	 * @param component component
	 * @param value value
	 * @return <code>{@link #PlanningPoint</code> as string
	 */
	public String getAsString(final FacesContext context, final  UIComponent component, final Object value)
			throws ConverterException {
		if (value != null) {
			return ((PlanningPoint) value).toString();	
		} else {
			return null;
		}
	}

Code:

Controller snippet
Code:
                public List<SelectItem> getGeoStartDropDownList() {
		return this.geoStartDropDownList;
	}


	public void setGeoStartDropDownList(final List<SelectItem> geoStartDropDownList) {
		this.geoStartDropDownList = geoStartDropDownList;
	}

	public PlanningPoint getGeoStartDropDownSelected() {
		return this.geoStartDropDownSelected;
	}

	public void setGeoStartDropDownSelected(final PlanningPoint geoStartDropDownSelected) {
		this.geoStartDropDownSelected = geoStartDropDownSelected;
	}
 
M

maki

Gast
Würde mich da jetzt ganz langsam rantasten, was passiert wenn getAsString immer "test" zurückgibt, ohne irgendetwas anderes zu machen?
 

y0dA

Top Contributor
Ich denke ich weiß schon wo das Problem liegt:
Also diese beiden <h:selectOneMenu> werden anfangs nicht gerenderd, erst wenn der User Eingaben gemacht hat und ich jene an ein Webservice geschickt habe werden diese gerenderd und der myFaces Lifecycle ist halt mal über cool und beim ersten mal, wo der converter aufgerufen wird, ist der value leer! Gibt es eine Möglichkeit das zu beheben?

**EDIT**
Ok, selbst wenn ich dem Konverter bei getAsString "test" zurückgeben lasse und bei getAsObject ein Dummy Objekt mit Werten, bekomme ich von facesstrace die error msg:
Code:
subViewPlanning:fPlanning:planGeoDropdown1: Validierungsfehler: Wert ist keine gültige Auswahl
 

y0dA

Top Contributor
Hier habe ich etwas interessantes zu meinem Problem gefunden: NABBLE-MYFACES-USER-FORUM

Auszug:

Ok, I think I figured out my problem. I'll summarize here in the hopes that this helps someone. I initially got the
solution half right, by implementing a Converter to convert between the String representation of an Employee and
an Employee object. However, I was still failing during the validation phase even though I had implemented a "null"
validator that always returned. The problem is that the UISelectOne class has a "validateValue" method that attempts
to validate that the selected item is in fact contained within the UISelectItems list. It does this by essentially calling
java.lang.Object.equals() on the selected object to compare it against items in the list. Even though all the data fields
within the Employee object instances were the same, they had different object references, and therefore were
deemed to be different. This ultimately led to a failure during validation. In order to solve this, I had to implement an
equals method of my own in the Employee class that overrides the default java.lang.Object.equals method. This
equals method compared the data fields within two Employee objects and declared them equal if all fields were equal.

To summarize, if you want to populate a UISelectOne control with a list of objects that are not simple Strings, you
need to do two things; implement a Converter to go back and forth between String and Object, and override
java.lang.Object.equals with your own equals implementation that compares the Object's data fields. Hope this helps
someone out there. Good luck finding this documented somewhere.


Konklusio: Ich habe die hashCode() und equals(Object obj) Methode überschrieben und siehe da, es funktioniert!

Nun habe ich nur mehr ein Problem hierbei:
Wenn ich nun in weiterer Folge den aktuellen Wert des selectOneMenu ändere (sprich ich wähle aus dem Dropdown
einen anderen Wert als den Defaultvalue aus) und jenes Objekt besitzt in einem String einen Umlaut dann bekomme
ich beim Converter (getAsObject) so einen String herein:
Code:
A;Stockerau;Oberzögersdorf;2000;;A-2000 Stockerau Oberzögersdorf (+++)

Wie kann ich das verhindern, denn somit bekomme ich wieder die Error Msg (Faces Trace):
Code:
subViewPlanning:fPlanning:planGeoDropdown1: Validierungsfehler: Wert ist keine gültige Auswahl
 

y0dA

Top Contributor
Ja!
Also wie oben schon erwähnt, wenn ich den Wert im Dropdown geändert habe und die Seite "refreshed" wurde (warum auch immer: Button und dergleichen) und eben jener Wert einen Umlaut beinhaltete, bekam ich in jenem Converter die Umlaute nicht korrekt dargestellt.

Lösung:
In der jeweiligen JSP inkludieren:
Code:
<%@ page pageEncoding="UTF-8"%>

Somit haben wir dieses Thema beendet :)

Eine Frage zu Convertern habe ich trotzdem noch:
Wann soll ich einen Converter benutzen(bei jedem Input Text etc)?
 
M

maki

Gast
EInen Converter kannst du immer benutzen wenn zB der eingegebene Text noch formatiert/geändert werden muss.
JSF bietet Standard Converter für die meisten java Datentypen (String, Date, etc.pp.)

Oder: Wenn du ein komplexes Objekt in der View anzeigen möchtest, zB eigene Datentypen ;)

Konverter sind sehr sehr nützlich in JSF!
 

y0dA

Top Contributor
Ok und eine Frage noch dann bin ich fertig:
Wenn mein Converter bzw dessen Methoden die ConverterException werfen wo kann ich die abfangen? Ich selbst rufe den Converter ja nicht auf!?
 
M

maki

Gast
Die Converter Exeption und die Validator Exception werden intern gefangen und als Messages weitergereicht.

Keine ERROR level logging, nur INFO level ;)

Deshalb ist FacesTrace so nützlich, kannst dir die messages anzeigen lassen ohne das message/s tag zu verwenden :)

Ist dir klar warum Converter wichtig sind?
Stell dir vor du hast ein enum, in der Managedbean arbeitest du nur damit, bekommst von der String represetation nix mit!
 

y0dA

Top Contributor
Denke schon dass ich Converter nun verstanden habe, jedoch sehe ich im Moment bei meinem Projekt kein allzu großes Einsatzgebiet hierfür, ausser eben bei den Dropdowns, da ich hier in die SelectItems das ganze Objekt packen möchte.

Ja und wenn ich diese Converter Error Msg nun ausgeben möchte wo soll ich das machen? Sprich in der JSP gibt es ein <message for=...>

Ich werde ja nicht im Converter selbst mir die Komponente holen müssen und dann den Errortext setzen? Oder doch?

Also wie komme ich dazu die Msg auszugeben?
 
M

maki

Gast
Es reicht das message tag zu verwenden, allerdings zeigt ein Message tag nur die messages für dies Komponente (for=..), das messages tag kann alle zeigen.
 

y0dA

Top Contributor
Also wenn ich in meinem Converter absichtlich (Testzweck) eine ConverterException werfe, dann bekomme ich keine Fehlermeldung sondern eine Fehlerseite mit dem tollen Stacktrace:
Code:
10:23:25,225 - ERROR javax.faces.webapp._ErrorPageWriter - An exception occurred
javax.faces.FacesException: Exception while calling encodeEnd on component : {Component-Path : [Class: javax.faces.component.UIViewRoot,ViewId: /jsp/secure/main.jsp][Class: org.apache.myfaces.custom.div.Div,Id: mainDiv3][Class: javax.faces.component.UINamingContainer,Id: subViewPlanning][Class: javax.faces.component.html.HtmlForm,Id: fPlanning][Class: javax.faces.component.html.HtmlPanelGrid,Id: planningPG5]}
	at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:559)
	at javax.faces.component.UIComponent.encodeAll(UIComponent.java:250)
	at javax.faces.component.UIComponent.encodeAll(UIComponent.java:246)
	at javax.faces.component.UIComponent.encodeAll(UIComponent.java:246)
	at javax.faces.component.UIComponent.encodeAll(UIComponent.java:246)
	at javax.faces.component.UIComponent.encodeAll(UIComponent.java:246)
	at org.apache.myfaces.application.jsp.JspViewHandlerImpl.actuallyRenderView(JspViewHandlerImpl.java:423)
	at org.apache.myfaces.application.jsp.JspViewHandlerImpl.renderView(JspViewHandlerImpl.java:380)
	at org.apache.myfaces.lifecycle.RenderResponseExecutor.execute(RenderResponseExecutor.java:41)
	at org.apache.myfaces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:140)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:152)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:147)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
	at java.lang.Thread.run(Thread.java:619)
Caused by: javax.faces.FacesException: Exception while calling encodeEnd on component : {Component-Path : [Class: javax.faces.component.UIViewRoot,ViewId: /jsp/secure/main.jsp][Class: org.apache.myfaces.custom.div.Div,Id: mainDiv3][Class: javax.faces.component.UINamingContainer,Id: subViewPlanning][Class: javax.faces.component.html.HtmlForm,Id: fPlanning][Class: javax.faces.component.html.HtmlPanelGrid,Id: planningPG5][Class: javax.faces.component.html.HtmlSelectOneMenu,Id: planGeoDropdown1]}
	at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:559)
	at org.apache.myfaces.shared_impl.renderkit.RendererUtils.renderChild(RendererUtils.java:515)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlGridRendererBase.renderChildren(HtmlGridRendererBase.java:221)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlGridRendererBase.encodeEnd(HtmlGridRendererBase.java:102)
	at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:556)
	... 25 more
Caused by: javax.faces.convert.ConverterException: Test Converter Exception - getAsString(..)
	at at.pcd.wam.gegenstandsbereich.tmcWeb.common.PlanningPointConverter.getAsString(PlanningPointConverter.java:82)
	at org.apache.myfaces.taglib.core.DelegateConverter.getAsString(DelegateConverter.java:101)
	at org.apache.myfaces.shared_impl.renderkit.RendererUtils.getConvertedStringValue(RendererUtils.java:621)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlRendererUtils.getSubmittedOrSelectedValuesAsSet(HtmlRendererUtils.java:362)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlRendererUtils.internalRenderSelect(HtmlRendererUtils.java:337)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlRendererUtils.renderMenu(HtmlRendererUtils.java:288)
	at org.apache.myfaces.shared_impl.renderkit.html.HtmlMenuRendererBase.encodeEnd(HtmlMenuRendererBase.java:57)
	at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:556)
	... 29 more

JSP:
Code:
<h:message for="planGeoDropdown1" styleClass="errorMsg" />
<h:panelGrid id="planningPG5" columns="2" border="1">
			<h:outputText value="#{labels.planStartDropdown}"
				styleClass="outputText"
				rendered="#{planningContrl.renderGeoDropDownStart}" />
			<h:selectOneMenu id="planGeoDropdown1"
				value="#{planningContrl.geoStartDropDownSelected}"
				rendered="#{planningContrl.renderGeoDropDownStart}">
				<f:converter converterId="planningPointConverter" />
				<f:selectItems value="#{planningContrl.geoStartDropDownList}" />
			</h:selectOneMenu>
			<h:outputText value="#{labels.planEndDropdown}"
				styleClass="outputText"
				rendered="#{planningContrl.renderGeoDropDownEnd}" />
			<h:selectOneMenu id="planGeoDropdown2"
				value="#{planningContrl.geoEndtDropDownSelected}"
				rendered="#{planningContrl.renderGeoDropDownEnd}">
				<f:converter converterId="planningPointConverter" />
				<f:selectItems value="#{planningContrl.geoEndDropDownList}" />
			</h:selectOneMenu>
		</h:panelGrid>

Also wie mache ich es richtig?
 
M

maki

Gast
Wann bitte sollte eine Converter Exception in der getAsString Methode aufgerufen werden ?
Eigentlich nie, schlieslich kann jeder notfalls die toString() Methode aufrufen, oder "" zurückgeben.

Es geht dabei haupsächlich um die getAsObject Methode, wirf doch mal dort ;)
 

y0dA

Top Contributor
Alles klar!

Danke dir - bis zum nächsten mal :D

**EDIT**

Code:
subViewPlanning:fPlanning:planGeoDropdown1: Ein Konvertierungsfehler ist aufgetreten.

Bekomme ich die Error Msg auch ohne:
Code:
subViewPlanning:fPlanning:planGeoDropdown1:
 
M

maki

Gast
Das message tag hat ein paar attribute, probier doch mal ein paar aus.
 

stevy

Neues Mitglied
Hallo,

mal eine Frage zu dem Sinn des Converters.

Wenn ich beim SelectItem das Objekt und eine Stringrepräsentation (das Label) angebe, wozu brauche ich dann noch einen Converter? Das Objekt ist doch schon im SelectItem gespeichert. Wenn sich die Selektion in der Combobox ändert, muss doch intern nur der "Zeiger" auf das aktuelle Objekt angepasst werden.

Ich würde dann erwarten, dass mir ein ValueChangeEvent über getNewValue() dieses aktuelle Objekt zurückgibt. Aber nein, stattdessen erhalte ich einen String, mit dem ich nicht viel anfangen kann. Bei komplexen Objekten mit Referenzen kann ich schlecht das Objekt aus einem String wiedererzeugen.

Verstehe es nicht. Hab ich irgendwo was übersehen?

Grüße, Stevy
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben