2011-07-30 12 views
9

Mam do czynienia z następującym problemem: na jednej stronie wyszczególniam wszystkich użytkowników mojej aplikacji i mam przycisk "edytuj" dla każdego z nich, który jest linkiem "GET" z ?id=<userid>.
jsf viewparam utracony po błędzie sprawdzania poprawności

Strona edycji zawiera metafory <f:viewParam name="id" value="#{editUserBean.id}"/>.
Jeśli popełniłem błędy przy wprowadzaniu i używam sprawdzania CDI Weld Bean), strona jest wyświetlana ponownie, ale straciłem adres URL w postaci ?id=..., przez co tracę identyfikator użytkownika, którego edytuję.

Przyjrzałem się podobnemu problemowi opisanemu w JSF validation error, lost value, ale rozwiązanie z inputhidden (lub, co gorsza, z tomahawkiem, który wygląda przesadnie) wymaga dużej ilości błędnego kodu.

Próbowałem dodać "Rozmowę" z CDI i działa, ale wygląda na to, że znowu przesadzam.

Czy istnieje proste rozwiązanie w JSF, aby zachować parametry podglądu w przypadku błędów sprawdzania poprawności?

[Moja okolica: Tomcat7 + MyFaces 2.1.0 + Hibernate Validator 4.2.0 + CDI (Weld) 1.1.2]

+3

używasz 'includeViewParams = TRUE w swojej złożenia skargi? – bluefoot

+0

Nie, ale mój program obsługi nie jest wywoływany: przesłanie kończy się niepowodzeniem w kroku sprawdzania poprawności. – DenisGL

Odpowiedz

7

ciekawy przypadek. Dla wszystkich, następujące minimalne kod reprodukuje to:

Facelet:

<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:h="http://java.sun.com/jsf/html" 
    xmlns:f="http://java.sun.com/jsf/core" 
> 

    <f:metadata> 
     <f:viewParam id="id" name="id" value="#{viewParamBean.id}"/> 
    </f:metadata> 

    <h:body> 

     <h:messages /> 

     #{viewParamBean.id} <br/> 

     <h:form> 
      <h:inputText value="#{viewParamBean.text}" > 
       <f:validateLength minimum="2"/> 
      </h:inputText> 

      <h:commandButton value="test" action="#{viewParamBean.actionMethod}"/> 
     </h:form> 

    </h:body> 
</html> 

Fasola:

@ManagedBean 
@RequestScoped 
public class ViewParamBean { 

    private long id;  
    private String text; 

    public void actionMethod() { 

    } 

    public long getId() { 
     return id; 
    } 

    public void setId(long id) { 
     this.id = id; 
    } 

    public String getText() { 
     return text; 
    } 

    public void setText(String text) { 
     this.text = text; 
    }  
} 

Jeśli wywołać Facelet z viewparam.xhtml?id=12 będzie wyświetlać 12 ekranie. Jeśli wprowadzisz coś ważnego, np. aaaaa, id zniknie z adresu URL, ale nadal będzie wyświetlany na ekranie (z uwzględnieniem stanowego charakteru komponentów interfejsu użytkownika).

Jednak ... jak podano w OP, jak tylko pojawi się jakikolwiek błąd weryfikatora (np. Wpisanie a), identyfikator zostanie trwale utracony. Wprowadzenie prawidłowych danych wejściowych nie spowoduje ich przywrócenia. Wygląda to prawie jak błąd, ale próbowałem zarówno Mojarra 2.1, jak i Myfaces 2.1 i oba mają takie samo zachowanie.

Aktualizacja:

Po pewnym kontroli, problem wydaje się być w tej metodzie `UIViewParameter” (Mojarra):

public void encodeAll(FacesContext context) throws IOException { 
    if (context == null) { 
     throw new NullPointerException(); 
    } 

    // if there is a value expression, update view parameter w/ latest value after render 
    // QUESTION is it okay that a null string value may be suppressing the view parameter value? 
    // ANSWER: I'm not sure. 
    setSubmittedValue(getStringValue(context)); 
} 

A potem dokładniej ta metoda:

public String getStringValue(FacesContext context) { 
    String result = null; 
    if (hasValueExpression()) { 
     result = getStringValueFromModel(context); 
    } else { 
     result = (null != rawValue) ? rawValue : (String) getValue(); 
    } 
    return result; 
} 

Ponieważ hasValueExpression() jest prawdą, spróbuje uzyskać wartość z modelu (backing bean). Ale ponieważ ten komponent bean był ograniczony zasięgiem, nie będzie miał żadnej wartości dla tego żądania, ponieważ sprawdzanie poprawności właśnie zakończyło się niepowodzeniem, a zatem żadna wartość nigdy nie została ustawiona.W efekcie wartość stanu UIViewParameter jest nadpisywana przez to, co back bean bean zwraca jako domyślną (zazwyczaj null, ale to zależy oczywiście od twojej fasoli).

Jednym z obejść jest utworzenie fasoli @ViewScoped, która i tak często jest lepszym zakresem (zakładam, że użyjesz tego parametru do pobrania użytkownika z usługi, i może nie trzeba tego robić w kółko przy każdym odesłaniu zwrotnym) .

Inną alternatywą jest utworzenie własnej wersji UIViewParameter, która nie próbuje uzyskać wartości z modelu, jeśli sprawdzanie poprawności zakończyło się niepowodzeniem (jak w zasadzie wszystkie inne komponenty UIInput).

+1

Dla zainteresowanych; Tworzę dla tego problem: http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-1029, proszę zagłosuj na niego, jeśli chcesz, aby zostało to naprawione. –

+0

Zauważyłem inne dziwne zachowanie wynikające z tego problemu (z Mojarra (2.1.2), ale nie z MyFaces (2.1.0)): jeśli parametr widoku jest ustawiony jako 'required =" true "', żądanie Ajax zawiedzie w fazie walidacji. – DenisGL

+3

FYI: łatany "UIViewParameter" jest dostępny jako '' z OmniFaces. Zobacz także http://showcase.omnifaces.org/components/viewParam – BalusC

1

rzeczywistości nie stracić parametr widoku. f: viewParam jest stateful, więc nawet jeśli nie ma go w adresie URL, nadal istnieje. Wystarczy umieścić punkt przerwania lub system.out w ustawieniu, aby wyświetlić parametr.

(jeśli google na viewParam bezstanowym Stateful znajdziesz trochę więcej informacji)

+0

Wielkie dzięki, znalazłem tutaj interesujący artykuł [link] (http://jdevelopment.nl/stateless-stateful-jsf-view-parameters/). Jednak nadal mam problem: jeśli zgłosisz mój formularz bez błędu, wywoływany jest setter związany z paramem widoku, ale jeśli popełnię błąd w moim wprowadzeniu i ponownie go przesłam, wartość zostanie utracona. – DenisGL

+1

Parametr UIViewParameter jest rzeczywiście stanowy, jak to opisuję w tym artykule, ale z powodu problematycznego kodu w metodzie 'encode' tego składnika, nadpisuje go wartością domyślną, która znajduje się w komponencie bean. Zobacz moją zaktualizowaną odpowiedź. –

+0

Dobra, zwykle jest to stanowy, ale z powodu pewnego nadzoru nad projektowaniem wartość jest rzeczywiście tracona, gdy pojawiają się błędy sprawdzania poprawności. Chyba nauczyłem się czegoś nowego! –

0

Mam to samo w mojej aplikacji. Przełączyłem się na @ViewAccessScoped, co pozwala na bardziej elegancką implementację.

0
<f:metadata> 
     <f:viewParam id="id" name="id" value="#{baen.id}"/> 
    </f:metadata> 

lub gdy po raz pierwszy dostać parametru z URL, zapisz go w sesji mapie i kontynuować korzystanie z tej mapy, a potem zapisać/lub zaktualizować mapę czystej formy.

0

Jest to trudne, ale można spróbować przywrócić parametry widoku z History API:

<?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:h="http://xmlns.jcp.org/jsf/html" 
     xmlns:f="http://xmlns.jcp.org/jsf/core" 
     xmlns:ui="http://xmlns.jcp.org/jsf/facelets"> 
    <f:metadata > 
     <f:viewParam name="param1" value="#{backingBean.viewParam1}" /> 
     <f:viewParam name="param2" value="#{backingBean.viewParam2}" /> 
     <f:viewAction action="#{view.viewMap.put('queryString', request.queryString)}" /> 
    </f:metadata> 
    <h:head> 
     <title>Facelet Title</title> 
    </h:head> 
    <h:body> 
     <ui:fragment rendered="#{facesContext.postback}" > 
      <script type="text/javascript"> 
       var url = '?#{view.viewMap.get('queryString')}'; 
       history.replaceState({}, document.title, url); 
      </script> 
     </ui:fragment> 
     <h:form> 
      <h:inputText id="name" value="#{backingBean.name}" /> 
      <h:message for="name" style="color: red" /> 
      <br /> 
      <h:commandButton value="go" action="#{backingBean.go}" /> 
     </h:form> 
     <h:messages globalOnly="true" /> 
    </h:body> 
</html>