JSF Komponenten programmatisch erstellen und auslesen.

aexon

Grünschnabel
Hallo zusammen,

im Rahmen meiner Master-Thesis verwende ich JSF als Technologie und bin heute auf ein Problem gestoßen, das ich nicht alleine gelöst bekomme. Suche nun seit Ewigkeiten im Netz, aber kein Ansatz ähnelt meinem.

Das Problem ist das folgende:

Ich möchte auf Basis eines dynamischen Datenbestandes Komponenten programmatisch erzeugen und anschließend auslesen. Soll heißen:

Ich habe auf Seiten der JSP ein PanelGrid und ein binding, so dass ich auf Managed-Bean-Ebene darauf zugreifen kann. Der Inhalt des PanelGrids wird nun mit Ausgaben und Eingabefelder zeilenweise gefüllt, alles auf Seite der Managed-Bean:

Das ganze sieht dann etwa so aus:

Java:
Application app = FacesContext.getCurrentInstance().getApplication();

for(JobTraining jobTraining : general) {
            HtmlOutputText number = (HtmlOutputText) app.createComponent(HtmlOutputText.COMPONENT_TYPE);
            number.setValue(i++);
            trainingGrid.getChildren().add(number); // trainingGrid ist das PanelGrid, auf das ich durch das Binding Zugriff habe

            HtmlOutputText costs = (HtmlOutputText) app.createComponent(HtmlOutputText.COMPONENT_TYPE);
            costs.setValue(jobTraining.getCosts().getSalaryValue());
            trainingGrid.getChildren().add(costs);

            HtmlOutputText duration = (HtmlOutputText) app.createComponent(HtmlOutputText.COMPONENT_TYPE);
            duration.setValue(jobTraining.getDuration());
            trainingGrid.getChildren().add(duration);

            /*
             * Select One Menu
             */

            UISelectOne quantity = (HtmlSelectOneMenu) app.createComponent(HtmlSelectOneMenu.COMPONENT_TYPE);
            quantity.setConverter(new IntegerConverter());

            List<SelectItem> items = new ArrayList<SelectItem>();
            for (int j = 0; j < qantityAdministrationLeaders; j++) {
                items.add(new SelectItem(j, Integer.toString(j)));
            }

            UISelectItems selectItems = (UISelectItems) app.createComponent(UISelectItems.COMPONENT_TYPE);
            selectItems.setValue(items);
            quantity.getChildren().add(selectItems);
            trainingGrid.getChildren().add(quantity);
            selectOnes.add(quantity);
        }

In Abhängigkeit der Anzahl der Elemente in der Collection werden die UI-Komponenten erzeugt und auch korrekt im Browser gerendert. Bis hierhin funktioniert alles wunderbar.

Wie zu sehen, wird je Zeile ein HtmlSelectOneMenu erzeugt. Wie komme ich nach dem Request an die vom Benutzer ausgewählten Werte in der Drop-Down-Liste ran?

Ich habe schon versucht, die HtmlSelectOneMenu(s) in einer Liste auf Session-Ebene zu speichern, aber das funkltioniert irgendwie nicht.

Danke im Voraus.

PS: eilt etwas => Abgabetermin naht. :)
 
Zuletzt bearbeitet:
Du kannst ein valueChangeListener anhängen, welcher dann aufgerufen wird.
Das erstellen der MethodExpression ist etwas speziell.
In der folgenden Funktion wird ein neues SelectOneMenu erstellt, die übergebene Collection wird für die SelectItems verwendet.
Hoffe du kannst damit was anfangen.

(Ich verwende Icefaces, aber ich glaub der Code is reines JSF)

Code:
	/**
	 * Erstellt ein SelectOneMenu aus der uebergebenen Liste von Elementen. 
	 * @param elements die elemente im Dropdown menu
	 * @return HtmlSelectOneMenu 
	 */
	private HtmlSelectOneMenu createSelectOneMenu(Collection<Irgendwas> elements) {
		HtmlSelectOneMenu menu = new HtmlSelectOneMenu();
		menu.setConverter(new IrgendwasConverter());
		menu.setPartialSubmit(true);
		Class<?>[] argtypes = new Class[1];
		argtypes[0] = ValueChangeEvent.class; 
		Application app = FacesContext.getCurrentInstance().getApplication();
		MethodExpression exp = app.getExpressionFactory().createMethodExpression(
				FacesContext.getCurrentInstance().getELContext(), "#{formularBean.valueChangedListener}", null, argtypes);
		menu.addValueChangeListener(new MethodExpressionValueChangeListener(exp));
		
		ArrayList<SelectItem> contents = new ArrayList<SelectItem>();
		for (Irgendwas p: elements) {
			SelectItem item = new SelectItem(p, p.getDescription());
			contents.add(item);
		}
		UISelectItems items = new UISelectItems();
		items.setValue(contents);
		menu.getChildren().add(items);
		return menu;
	}
 
Zuletzt bearbeitet:
Hallo und Danke schon einmal für deine Antwort.

Deinen Code konnte ich erfolgreich in meinen einbinden, nur verstehe ich noch nicht so ganz, wie mir das weiterhelfen wird. Wenn ich dich richtig verstehe, wird nun bei jeder Änderung, die an einer der Drop-Down-Listen durch den Anwender getätigt wird, ein ValueChangeEvent gefeuert, richtig?

Sollte ich diese Änderung dann nicht mit einer Methode

Java:
public void valueChangedAttributes(ValueChangeEvent vce) {
    logger.info("changed");
}

gemeldet bekommen? (Deinen Ausdruck "formularBean.valueChangedAttributes" habe ich durch meine Managed-Bean ersetzt.) Das tut es bei mir nämlich leider nicht. Sorry, dass ich gerade etwas auf dem Schlauch stehe, aber mit MethodExpression habe ich bisher noch nichts zu tun gehabt.

Angenommen, der Event WÜRDE gefeuert, wie käme ich denn dann an den vom Benutzer ausgewählten Wert heran?

Danke und viele Grüße

PS: Die Methode setPartialSubmit(boolean); ist ICEFaces-spezifisch.
 
Zuletzt bearbeitet:
Wenn ich dich richtig verstehe, wird nun bei jeder Änderung, die an einer der Drop-Down-Listen durch den Anwender getätigt wird, ein ValueChangeEvent gefeuert, richtig?

So war das gedacht ja.
Mit event.getNewValue() bekommst du den neuen Wert der selektiert wurde.
Code:
	public void valueChangedAttributes(ValueChangeEvent event) {
		log.info("valueChangedAttributes newValue: " +event.getNewValue().toString());
	}

Im Prinzip machts ja nichts anderes als der folgende code im jsp.
Code:
        <ice:selectOneListbox
          valueChangeListener=#{formularBean.valueChangedListener}"
        </ice:selectOneListbox>

Im 'normalen' JSF wird der Listener erst ausgeführt, wenn das Formular submitted wird.
Ausser du machst noch ein onChange="submit();" rein.
 
Nochmals danke für deine Antwort.

Dein Vorschlag ist gut. So hätte es sicherlich funktioniert. Wenn der Anwender keinen Wert ausgewählt hätte - und demnach kein VCE gefeuert worden wäre - hätte ich einfach einen neutralen Wert als Auswahl genommen (in diesem Fall 0).

Mittlerweile bin ich auf eine dataTable umgestiegen. Das hat auf der Bean-Seite den Vorteil, dass ich weniger Code habe. Und dadurch, dass ich die Tabelle mit einer List<Wrapper> füttere, habe ich die Daten nach dem Request direkt an der Angel (eine Property des Wrappers ist eine Liste mit SelectItem(s) und eine weiter der vom Anwender gewählte Wert).

Danke für deine Überlegungen.

Grüße
 

Neue Beiträge

Zurück