Scopes - Daten in JSF-Formular anlegen/bearbeiten, Felder vorbelegen

Tatsu

Mitglied
Hallo,
ich möchte ein JSF-Formular benutzen, um einen neuen Datensatz anzulegen oder einen bestehenden anzuzeigen/ zu bearbeiten. Wird ein Datensatz neu angelegt, so sollen einige Felder des Formulars mit Werten vorbelegt werden. Ich möchte dazu Java EE 6 Techiken (JSF, CDI, EJB, JPA) verwenden, also kein Seam, Spring, etc.

Szenario:
  • Ein JSF-Formular soll zum Anlegen, bzw. Bearbeiten eines Datensatzes verwendet werden.
  • Wird ein neuer Datensatz angelegt, so sollen einige Formularfelder mit Werten vorbelegt werden.
  • Ändert man einige dieser Werte und kann das Formular nicht gespeichert werden (z.B. wegen fehlerhafter Eingabe) so sollen die eingetragenen Werte erhalten bleiben.
  • Wird ein bestehender Datensatz aufgerufen, so sollen die Werte aus der Datenbank angezeigt werden. Die Methode zum Vorbelegen der Felder soll also nicht ausgeführt werden.

Ich habe die Annotationen [c]@ManagedBean[/c] in Verbindung mit [c]@RequestScoped[/c] und [c]@SessionScoped[/c] verwendet und bin mit beiden nicht zum Ziel gekommen. Für die Vorbelegung der Felder benutze ich eine Methode die ich mit [c]@PostConstruct[/c] annotiere.

Konkrete Fragestellung:
  • Wie setzt man sowas in EE 6 um (Best Practice)?
  • Muss man dafür [c]@ConversationScoped[/c] verwenden, oder macht man das besser anders?

Aufgetretene Probleme mit den Annotationen:
@ViewScoped
Mit @ViewScoped funktioniert alles richtig beim Anlegen eines neuen Datensatzes. Die mit @PostConstruct annotierten Methode wird beim ersten Aufruf des Formulars aufgerufen und die Felder werden korrekt mit Werten vorbelegt. Ändert man nun einige Werte so bleiben diese auch erhalten, wenn das Formular mehrfach wieder aufgerufen wird (z.B. bei Fehleingaben).

Wenn ich allerdings in einer Liste einer anderen JSF-Seite einen Datensatz zum Bearbeiten auswähle, dann werden die Daten nicht in das Formular geladen, da ich ja den Seitenkontext gewechselt habe. Das Objekt wird also neu erzeugt. Statt den ausgewählten Datensatz anzuzeigen, wird ein neues Objekt erstellt.

@RequestScoped
Also habe ich es mit der Annotation @RequestScoped versucht. Nun werden die Daten korrekt im Formular angezeigt, wenn ich in der Liste einen Datensatz zum Bearbeiten ausgewählt habe.

Ändere ich nun allerdings einige dieser Werte und es treten Fehler beim Prüfen des Formulars auf (z.B. bei Fehleingaben) wird die mit @PostConstruct annotierte Methode aufgerufen. Diese füllt dann die Felder mit den Standardwerten, was ja nicht sein soll.

Ich bin ratlos wie ich das korrekt umsetzen soll und bin übrigens Java EE Newbie.

Auszug Formular [license.xhtml]:
[XML]
...
<h:eek:utputLabel value="Erstellt am" for="createdAt" />
<h:inputText id="createdAt" value="#{licenseController.license.createdAt}" readonly="true" >
<f:convertDateTime type="date"/>
</h:inputText>
<p:message for="createdAt" />

<h:eek:utputLabel value="Erstellt von" for="createdBy" />
<h:inputText id="createdBy" value="#{licenseController.license.createdBy}" readonly="true" />
<p:message for="createdBy" />

</h:panelGrid>
</p:fieldset>

<p:commandButton action="#{licenseController.doCreateLicense}" value="Speichern" update="form" />
[/XML]

Auszug Liste (Edit-Button) [licenses.xhtml]:
[XML]
<p:column id="editButton">
<p:commandButton id="edit" action="#{licenseController.doEditLicense}" ajax="false" image="ui-icon ui-icon-search">
<f:setPropertyActionListener value="#{license}" target="#{licenseController.selectedLicense}" />
</p:commandButton>
</p:column>
[/XML]

Auszug LicenseController.java:
Java:
@ManagedBean
@RequestScoped
public class LicenseController implements Serializable {

    @EJB
    private LicenseEJB licenseEJB;
    private License license = new License();
    private List<License> licenseList = new ArrayList<License>();
    private License selectedLicense;

    @PostConstruct
    public void doConstruct() {
        // DS für Tabelle auslesen
        this.licenseList = licenseEJB.findAll();
        // Formularfelder vorbelegen
        this.license.setRegKey(getRandomKey());
        this.license.setCreatedAt(new Date());
        this.license.setCreatedBy("Captain Kirk");
        this.license.setMaxLicenses(1);
    }

    public String doCreateLicense() {
        this.license = licenseEJB.createLicense(license);
        return "licenses.xhtml?faces-redirect=true";
    }

    public String doEditLicense() {
        this.license = this.selectedLicense;
        return "license";
    }
// ... getter und setter
}

Vielen Dank im Voraus für Eure Tipps.

Bastian
 

Tatsu

Mitglied
Hallo alle zusammen,

ich habe jetzt noch mal einige Tage lang gegoogelt, in meinen Büchern nachgeschaut und Beispiele ausprobiert. In den Beispielen wird meist SessionScoped verwendet. Das kann aber eigentlich doch nicht richtig sein, oder?

Ich vermute, das bei Formularen in den meisten Fällen @ViewScoped richtig sein sollte. Beim Anlegen von Daten funktioniert das auch super. Ich bekomme es nur einfach nicht hin, Daten zum Bearbeiten in ein Formular zu laden.

Kann mir jemand von Euch vielleicht ein kleines Beispiel mit einer Managed Bean oder CDI geben, oder einen Tip der mich auf den richtigen Weg bringt?

Ich wäre Euch sehr dankbar, da ich mich seit Tagen im Kreis drehe und überhaupt nicht voran komme.

Die Varianten die ich umsetzen möchte:
1. Neuer Datensatz -> Formular mit einigen vorbelegten Feldern (Standardwerte) zum Anlegen
2. Liste von Datensätzen -> Mausklick auf einen Datensatz lädt -> Formular mit dem Datensatz zum Editieren

Bastian (Java EE Newbie)
 
Zuletzt bearbeitet:

y0dA

Top Contributor
Da ich aktuell nur mit JSF 1.2 inkl. Richfaces arbeite kann ich dir nur bedingt weiterhelfen, dein Problem ist einfach dass wenn du auf der Seite ein submit absetzt die Seite neu geladen wird und hierbei eben die dazugehörige ManagedBean neu gebaut.

Mit Richfaces kann man das Problem mit dem Tag
Java:
<a4j:keepAlive beanName="managedBean" />
umgehen - damit bleibt die Bean so lange "am Leben" bis die Seite verlassen wird.
Alternative wäre noch dass du manuell dein License Objekt in die Session schreibst und aus der Session wieder löschst wenn du die Seite verlässt bzw das Objekt nicht mehr benötigst. Hierbei kannst du dann bequem das Objekt aus der Session holen in der @PostConstruct Methode der ManagedBean.
 
Zuletzt bearbeitet:

Tatsu

Mitglied
Ich zeige im Folgenden zwei Lösungen für das von mir in diesem Thread beschriebene Problem auf.

  • Lösung mit JSF-Boardmitteln
  • Lösung mit PrettyFaces

Sollte Euch die Lösung gefallen, bzw. weiterhelfen, so würde ich mich über einen Danke-Klick freuen. Es wäre schön, wenn Ihr Verbesserungsvorschläge machen, bzw. Ideen einbringen würdet, wie man das Ganze evtl. noch besser umsetzen könnte.


Einleitung:
Möchte man mit [c]@ViewScoped[/c] annotierten [c]ManagedBeans[/c] arbeiten, so ergibt sich das Problem, dass die ManagedBean zerstört wird, sobald man den Seitenkontext verlässt.

Möchte man z.B die eindeutige [c]ID[/c] eines Datensatzes auf die nächste JSF-Seite retten, so stellt sich die Frage, wie man dies am besten umsetzt.

Szenario:
Code:
┏━━━━━━━━━━━━━━━━━━━━━━━┓                       ┏━━━━━━━━━━━━━━━━┓
┃Seite-> personen.xhtml                ┃                       ┃Seite-> person.xhtml       ┃
┃┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┃ Bei Klick auf Button  ┃Vorname:    ┆J.T.         ┃
┃Max      ┆Mustermann   ┆ [Anzeigen]  ┃ Person anzeigen       ┃Nachname:   ┆Kirk         ┃
┃Gabi     ┆Glorreich    ┆ [Anzeigen]  ┃ =====================>┃Geburtstag: ┆01.01.2011   ┃
┃J.T.     ┆Kirk         ┆ [Anzeigen]  ┃                       ┃Geburtsort: ┆Erde         ┃
┗━━━━━━━━━━━━━━━━━━━━━━━┛                       ┗━━━━━━━━━━━━━━━━┛
                                        ║                        ║
                                      ┏━━━━━━━━━━━━━━━━┓
                                      ┃ PersonController
                                      ┃ @ManagedBean
                                      ┃ @ViewScoped
                                      ┗━━━━━━━━━━━━━━━━┛

Lösung mit JSF-Boardmitteln:
Eine Möglichkeit dazu stellt JSF selbst zur Verfügung. Benutzt man den Tag [c]<f:viewParam />[/c] auf der Zielseite [c]person.xhtml[/c], so kann man den benötigten Wert einfach übergeben und dann im Controller auslesen. Die entsprechende Methode kann man auch gleich in der Seite mit [c]<f:event type="javax.faces.event.PreRenderViewEvent" ... />[/c] angeben.

Auszug person.xhtml
HTML:
<f:metadata>
  <f:viewParam name="id" value="#{personController.id}"/>
  <f:event type="javax.faces.event.PreRenderViewEvent" 
           listener="#{personController.prepareForm}"/>
</f:metadata>

Auszug personController.java
Java:
public void prepareForm(ComponentSystemEvent ev) {
        FacesContext ctx = FacesContext.getCurrentInstance();
        if (!ctx.isValidationFailed()) {
            if (id != 0) {
                // Find record by id
                this.person = personEJB.findById(id);
            } else {
                // Set default values for the new record
            }
        }
    }

Lösung mit PrettyFaces:
Löst man das Ganze mit PrettyFaces so ergibt sich ganz nebenbei noch der Vorteil, dass URls ähnlich wie beim Einsatz einer REST-Architektur genutzt werden können. Der Aufruf der URL [c]/editor/person/50[/c] führt dann dazu, dass die Person mit der id 50 angezeigt wird.

Funktion|Seite|URI|PrettyFaces-Annotation
Personenliste anzeigen|/editor/personen.xhml|/editor/personen|pattern = "/editor/personen/", viewId = "/editor/personen.xhtml"
Person anlegen|/editor/person.xhtml|/editor/person|pattern = "/editor/person/", viewId = "/editor/person.xhtml"
Person anzeigen|/editor/person.xhtml|/editor/person/20|pattern = "/editor/person/#{ id : personController.id }" ...

Wie im letzten Mapping ersichtlich besteht die Möglichkeit Variablen anzugeben die in Attribute der Bean geschrieben werden sollen. Diese Möglichkeit wird dazu genutzt, um die ID an die Bean zu übergeben.

Auszug personController.java
Java:
@ManagedBean
@ViewScoped
@URLMappings(mappings = {
    @URLMapping(id = "listPersons", pattern = "/editor/personen/", viewId = "/editor/personen.xhtml"),
    @URLMapping(id = "newPerson", pattern = "/editor/person/", viewId = "/editor/person.xhtml"),
    @URLMapping(id = "editPerson", pattern = "/editor/person/#{ id : personController.id }", viewId = "/editor/person.xhtml")
})
public class PersonController implements Serializable {
...
}

Zum Schluss wird eine Methode mit [c]@URLAction(mappingId = "editPerson", onPostback = false)[/c] annotiert, um den Datensatz der gewünschten Person zu laden.

Auszug personController.java
Java:
    @URLAction(mappingId = "editPerson", onPostback = false)
    public String loadEntity() {
        int id = 0;

        FacesContext ctx = FacesContext.getCurrentInstance();
        if (!ctx.isValidationFailed()) {
            // Convert String to integer
            if (rid != null) {
                try {
                    id = Integer.parseInt(rid);
                } catch (Exception E) {
                    // Redirect to create context if rid is not a number
                    ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Identifier must be a number.", "Identifier must be a number. Example: 1435"));
                    return "<seiteA>"
                }
            }
            if (id != 0) {
                // Redirect to create context if there is no record with the current rid
                if (getEJB().findById(id) == null) {
                    ctx.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN, "There is no record with the id " + id + ".", ""));
                    return "<seiteA>";
                } else {
                    // Get Record
                    this.entity = (T) getEJB().findById(id);
                }
            } else {
                return "<SeiteA>";
            }
        }
        return null;
}

Bei der hier vorgestellten Lösung macht es Sinn, die Bean von einer generischen abstrakten Bean abzuleiten, um sich die Tipparbeit nur einmal zu machen.

Ich hoffe dieser Artikel hilft anderen die das gleiche Problem haben weiter. Über einen Danke-Klick würde ich mich freuen.

Grüße Bastian
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
looparda JPA Persistenz vom in GUI veränderten Daten-Modell Allgemeines EE 7
S JSP Zwischen zwei Formularen Daten austauschen Allgemeines EE 0
P Daten von HTML and JSP schicken Allgemeines EE 0
D Apache POI Probleme mit Daten(Datum) die aus Formeln entstehen Allgemeines EE 3
C JSF Bestimmte Daten aus der Datenbank anzeigen lassen Allgemeines EE 13
M Daten aus der Resource werden nicht übernommen Allgemeines EE 4
H SQL Daten von Webservice an Client übergeben Allgemeines EE 3
F Servlet Daten im Speicher ablegen Allgemeines EE 3
A Anfängerfrage: daten in datenbank speichern Allgemeines EE 8
K Daten aus ApplicationServer auf Website darstellen Allgemeines EE 5
C daten von php zu jsp Allgemeines EE 3
MQue Server -> Client zyklische Daten senden Allgemeines EE 20
W Daten mit j2ee aus datenbank abfragen Allgemeines EE 8
M Daten aus JavascriptSeite von Java auswerten lassen Allgemeines EE 3
I Über Formular Daten zu Servlet Allgemeines EE 36
B Session Daten pro User merken Allgemeines EE 9
M EJB Löschen von DB-Daten beim Deployen verhindern Allgemeines EE 2
B JSF - selectOneMenu mit Daten aus faces-config füllen Allgemeines EE 5
J Socket daten darstellen per jsp,servlet Allgemeines EE 2
S Downloadbox auch ohne Daten erzwingen // Content-Disposition Allgemeines EE 6
S Daten in Java schreiben und PHP lesen Allgemeines EE 8
L speichern von daten mittels servlet in xml Allgemeines EE 8
P Tomcat Servlet POST Daten als Array Allgemeines EE 2
S Best-Practice? Daten über Tier-Grenzen hinweg? Allgemeines EE 2
V Bean-Daten in JSF-JSP finden Allgemeines EE 3
D Bekomme DAten von einen Jsp nicht in den Tag Handler Allgemeines EE 2
S JSP - geschichtliche Daten Allgemeines EE 4
F Session Bean -> Daten aus dem Servlet holen Allgemeines EE 11
D Abfrage der header daten funktionieren nicht. Allgemeines EE 2
G Daten aus Inputfeldern in Tabelle speichern Allgemeines EE 6
A JSF - Daten in Session speichern Allgemeines EE 2
S Daten in ein Excel file exportieren Allgemeines EE 3
S Post und Get Daten Allgemeines EE 5
clemson Daten aus Email holen Allgemeines EE 4
J Formular aktualisieren-Daten werden erneut in DB geschrieben Allgemeines EE 6
H daten in session speichern Allgemeines EE 8
A Tabstopp-getrennte Daten üb. Webformular in Datenbank laden! Allgemeines EE 2
T Daten aus der Webseite (JSP) als .txt speichern Allgemeines EE 8
M servlet daten einlesen -> hashmap speichern Allgemeines EE 3
M Speicherung von Daten und JSP Allgemeines EE 9
R HTML Formular Allgemeines EE 3
Streeber JSF Formular Überprüfung -> TF markieren Allgemeines EE 2
S JSP Parameter mit Formular übergeben Allgemeines EE 2
G Framework für selbsterstellte Formular und Seiten gesucht Allgemeines EE 3
K Objekte in ein neues Formular laden und dort verändern JSF? Allgemeines EE 8
H File Upload per Formular - prinzipielle Vorgehensweise? Allgemeines EE 2
N Über Formular Bild auf FTP uploaden u. Strings in ne DB Allgemeines EE 3
M Online Formular Allgemeines EE 7
K Servlet per Formular aufrufen Allgemeines EE 8
S JSP/Servlet -> Einzelne Zeilen aus DB in Formular darstel Allgemeines EE 19
H Parameterübergabe ohne Formular? Allgemeines EE 7
B Formular Allgemeines EE 25

Ähnliche Java Themen

Neue Themen


Oben