JSF ViewScoped - Zustand verliert sich nach Seitenaktualisierung

Dieses Thema JSF - ViewScoped - Zustand verliert sich nach Seitenaktualisierung im Forum "Web Tier" wurde erstellt von Txxxxs, 5. März 2011.

Thema: ViewScoped - Zustand verliert sich nach Seitenaktualisierung Hallo, es geht um folgenden Quelltext: ... import javax.annotation.PostConstruct; import...

  1. Hallo,

    es geht um folgenden Quelltext:
    Code (Java):

    ...
    import javax.annotation.PostConstruct;
    import javax.faces.application.FacesMessage;
    import javax.faces.bean.ManagedBean;
    import javax.faces.bean.ManagedProperty;
    import javax.faces.bean.ViewScoped;
    import javax.faces.context.FacesContext;
    ...
    @ManagedBean
    @ViewScoped
    public class ArtikelAnlegen {

        @ManagedProperty(value = "#{index.facade}")
        private PersistenceFacade facade;
        private Artikel artikel;
        private Vector<ArtKategorie> artkategorien;

        public ArtikelAnlegen() {
            artkategorien = ArtKategorie.listArtKategorien();
        }

        @PostConstruct
        public void postArtikelAnlegen() {
            if (artikel == null) {
                try {
                    artikel = facade.createArtikel();
                } catch (DAOException e) {
                    e.printStackTrace();
                }
            }
        }

        public void setFacade(PersistenceFacade facade) {
            this.facade = facade;
        }

        public Vector<ArtKategorie> getArtkategorien() {
            return artkategorien;
        }

        public Artikel getArtikel() {
            return artikel;
        }

        public String speichern() {
            try {
                facade.save(artikel);
                facade.commit();
            } catch (DAOException e) {
                e.printStackTrace();
            }
            FacesMessage message = new FacesMessage(
                    "Neuer Artikel erfolgreich angelegt!");
            FacesContext.getCurrentInstance().addMessage(null, message);
            return "/wartung.xhtml";
        }

    }
     
    Code (Java):

    <?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:f="http://java.sun.com/jsf/core"
        xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Artikel anlegen</title>
    </h:head>
    <h:body>
        <h1>
            <h:outputText value="Verkaufssystem" />
        </h1>
        <h2>
            <h:outputText value="Wartungsbereich" />
        </h2>
        <h3>
            <h:outputText value="Artikel anlegen" />
        </h3>
        <h:form>
            <h:panelGrid columns="3">
                <h:outputLabel for="artname">Artikelname</h:outputLabel>
                <h:inputText id="artname" value="#{artikelAnlegen.artikel.artname}"
                    required="true">
                    <f:ajax event="blur" render="artnameMessage" />
                </h:inputText>
                <h:message id="artnameMessage" for="artname" />

                <h:outputLabel for="kurzbeschr">Kurzbeschreibung</h:outputLabel>
                <h:inputTextarea id="kurzbeschr"
                    value="#{artikelAnlegen.artikel.kurzbeschr}" required="false">
                    <f:ajax event="blur" render="kurzbeschrMessage" />
                </h:inputTextarea>
                <h:message id="kurzbeschrMessage" for="kurzbeschr" />

                <h:outputLabel for="preis">Preis</h:outputLabel>
                <h:inputText id="preis" value="#{artikelAnlegen.artikel.preis}"
                    required="true">
                    <f:ajax event="blur" render="preisMessage" />
                </h:inputText>
                <h:message id="preisMessage" for="preis" />

                <h:outputLabel for="selectartkategorie">Artikelkategorie</h:outputLabel>
                <h:selectOneMenu id="selectartkategorie"
                    value="#{artikelAnlegen.artikel.artkatnr}" required="true">
                    <f:selectItems value="#{artikelAnlegen.artkategorien}" var="artkat"
                        itemLabel="#{artkat.name}" itemValue="#{artkat.artkatnr}" />
                    <f:ajax event="blur" render="selectartkategorieMessage" />
                </h:selectOneMenu>
                <h:message id="selectartkategorieMessage" for="selectartkategorie" />

                <h:panelGroup />
                <h:commandButton value="Speichern"
                    action="#{artikelAnlegen.speichern}">
                    <f:ajax execute="@form" render="@form" />
                </h:commandButton>
                <h:messages globalOnly="true" layout="table" />
            </h:panelGrid>
        </h:form>
    </h:body>
    </html>
     
    Die Seite funktioniert und macht das, was sie soll - das Problem:
    Ist die Seite geöffnet - egal ob mit oder ohne eingegebene Daten - und aktualisiert man sie über die Taste F5 bzw. die entsprechende Webbrowser-Schaltfläche, dann bleiben zwar die Werte in den Eingabefeldern erhalten, jedoch scheint diese Bean neu erzeugt zu werden, da die Attribute wieder auf null stehen und die Methode createArtikel() aufgerufen wird. Bei jedem Neuladen der Seite ergibt sich das gleiche Verhalten.
    Warum bleibt diese ManagedBean nicht entsprechend der annotierten Lebensdauer (ViewScoped, also gebunden an die Ansicht, also erst nach Verlassen der Seite ungültig) bestehen?

    Weiterhin hätte ich eine Grundsatzfrage zu den Scopes:
    Angenommen, ich habe eine ManagedBean, die ein Kunde-Objekt enthält, mit dem ich in einigen folgenden Seiten arbeiten möchte, irgendwann möchte ich auf die Startseite (index.xhtml) (zurück-)navigieren und besagte ManagedBean erneut nutzen, um ein anderes Kunde-Objekt anzulegen usw. Soweit ich es verstanden habe, wäre für diese Bean @SessionScoped angebracht, da ich über mehrere Seiten navigiere und Daten nutzen möchte. Was aber ist, wenn ich von meiner Startseite aus obige ManagedBean neu haben möchte, um z. B. beim Erstellen eines neuen Kunden nicht die Daten von vorher zu haben? Wie geht man in so einem Fall standardmäßig vor? Kann man eine ManagedBean auch für ungültig erklären bzw. explizit neu erstellen lassen?


    Danke im Voraus! :)
     
  2. Vielleicht hilft dir das Java-Tutorial weiter. Hier klicken --> (Klick)
  3. Weiß niemand, warum sich ViewScoped bei obiger ManagedBean scheinbar wie RequestScoped verhält?
    Ich kann den Fehler einfach nicht finden, habe testweise sogar die ajax-Tags sowie die Navigation entfernt, aber es ändert sich nichts, die ManagedBean wird bei jedem Neuladen der Seite neu erstellt.
    Ich verwende übrigens Eclipse IDE for Java EE Developers 3.6.2, Apache Tomcat 7.0.8 und Mojarra 2.0.3.

    Ändere ich den Gültigkeitsbereich in SessionScoped, passiert das nicht, jedoch habe ich dann das Problem, dass ich nicht weiß, wie ich bei jedem neuen Aufruf dieser Seite eine neue ManagedBean bekomme. Sprich, ich habe eine Seite wartung.xhtml und möchte von dieser auf artikelAnlegen.xhtml navigieren, dort wie oben zu sehen Daten eingeben/speichern oder abbrechen, auf jeden Fall möchte ich wieder auf wartung.xhtml zurückkommen und die Möglichkeit haben, erneut diese Seite aufzurufen, um einen weiteren Artikel anzulegen.
    Gibt es keine Annotation/Methode/Befehl o.ä., um das zu lösen? :(
    Was macht ihr, wenn eine MangedBean länger als die Ansicht, aber kürzer als die Session verfügbar sein soll bzw. so lange halten soll, wie man sie explizit benötigt?
     
  4. ViewScope ist nur gültig solange du auf der selben Seite bleibst. RequestScope nur während eines einzigen Requests. Das heißt der ViewScope gilt nur solange du auf deiner "wartung.xhtml" Seite bleibst.
    Um Daten über mehrere Views, aber kürzer als SessionScope zu halten, bietet sich nur eine Conversation an.

    Zu Beispiel mit Myfaces Orchestra.
    kurz Einführung zu Orchestra
    Beispiel dazu
     
    Zuletzt bearbeitet: 7. März 2011
  5. Das klingt ja ganz gut, aber was macht man, wenn man auf Mojarra setzt und sonst erst mal nichts anderes nutzen möchte? Mit zusätzlich MyFaces Orchestra scheint es ja nicht getan zu sein, man braucht darüber hinaus noch Spring.
    Gibt es keine gängige Lösung, eine SessionScoped-ManagedBean zu löschen?

    Hinsichtlich ViewScoped habe ich erfahren, dass ein Neuladen (Taste F5 usw.) einer Webseite eine neue Anfrage darstellen würde und deshalb eine mit @ViewScoped annotierte ManagedBean neu erstellt wird - ich hätte gedacht, sie bleibt so lange bestehen, wie man eben die Seite vor sich hat, egal ob und wie oft man sie aktualisiert.
     
  6. Habe das gleiche Problem. Hast du da eine Lösung? Ich arbeit mit Tomcat und PrimeFaces 4.0, würde auf zusätzliche Bibliotheken gerne verzichten.

    Danke.
     
  7. stg
    stg
    Welches "Problem" meinst du jetzt? Die ursprüngliche Frage wurde doch geklärt...
     
  8. Nun, mein Problem ist wie folgt:

    Von der View A (z.B. Tabelle mit Kundendaten) muss ich zu View B (z.B. Kundendetails). Dabei gebe ich den ausgewählten Kunden mit:

    Code (Java):
    FacesContext.getCurrentInstance().getExternalContext().getFlash().put("selectedKunde", kunde);
     
    In der View B hole ich die kunde-Bean raus:

    Code (Java):
    Kunde kunde = (Kunde)FacesContext.getCurrentInstance().getExternalContext().getFlash().get("selectedKunde");
     
    Das geht so lange gut, bis man bei der Anzeige der View B eine Aktualisierung veranlasst (z.B. F5). Da die Übergabe der Kunde-Bean jetzt fehlt, gibt es nichts zum Anzeigen, oder werden gar Exceptions geworfen.

    Da ich mit Tomcat arbeit und auf zusätzliche Bibliotheken gerne verzichten würde, kann ich 'conversation scope' nicht nutzten, meine Beans beleiben weiterhin ViewScoped. Zurzeit helfe ich mir mit der Ablage von Beans in die SessionMap.

    Da auch in meinem Fall der Zustand der ViewScoped-Bean nach einer Aktualisierung verloren geht , habe ich hier meine Frage gestellt. Denn, die Ablage von Params in SessionMap halte ich für eine Notlösung und wäre froh, wenn ich darauf verzichten könnte. Weiss allerdings im Moment nicht, wie ich es anders machen könnte.
     
  9. stg
    stg
    ViewScoped Bean liegen aber nunmal im ViewScope ... wenn der Scope nicht ausreicht, muss man einen längerlebigen Scope wählen. Da gibt es nicht viel Spielraum...

    Möglicherweise kannst du deine benötigten Objekte innerhalb voin View B in einer Pre Destroy Methode erneut in den Flash Scope packen, sodass du sie bei erneutem Laden wieder verfügbar hast. Ich bin mir aber nicht sicher, ob das nicht ggfls Race Condition Probleme verursacht. Schön klingt das jedenfalls auch nicht.

    Du kannst den User natürlich auch einfach wieder auf View A leiten, wenn die Eingabe von "kunde" fehlt und ihn freundlich darauf hinweisen, dass es das Manual lesen soll und nicht so einen Unfug anstellen soll :)

    Alle weiteren Lösungen, auch wenn du mit CustomScoped arbeiten solltest, laufen zwangsläufig darauf hinaus, dass du in irgendeiner Art und Weise auf den Session Scope ausweichen musst.

    Oder aber, falls das in Frage kommt, du merkst dir die Daten Client-seitig. Find ich aber auch nicht wirklich schön.

    Eine wirkliche Lösung kann ich, wie du siehst, also auch nicht direkt anbieten. Aber vielleicht konnte ich ja ein paar Gedankengänge anregen :)
     
    Zuletzt bearbeitet: 9. März 2015
  10. Ja, das ist so. Vielen Dank.
     
  11. Schau dir jetzt hier den Kurs an und lernen Java zu programmieren: --> Hier klicken, um mehr zu erfahren (Klick)