2012-05-23 5 views
5

In meiner JSF 2-basierten Anwendung habe ich ein Formular, das (neben anderen UI-Komponenten) einige Kontrollkästchen enthält.Nach Validierung Fehler nachfolgende Ajax-Anfragen erhalten Werte von UI-Komponenten und nicht von Beans

Auf den Kontrollkästchen habe ich ajax Anfragen registriert, die ausgelöst werden, wenn sie überprüft werden. Die Ajax-Anfragen aktualisieren nur den Wert eines anderen Kontrollkästchens in der Backing-Bean. Als Ergebnis wird auch das andere Kontrollkästchen aktiviert (wenn es erneut gerendert wird - da es den aktualisierten Wert von der Backing Bean in der Renderantwortphase benötigt).

Dies funktioniert gut, bis das gesamte Formular übermittelt und Validierungsfehler auftreten. Dann funktionieren die Ajax-Anfragen immer noch und ändern den Wert für die Backing-Bean, aber in der Phase des erneuten Renderns des aktualisierten Kontrollkästchens wird der Wert nicht von der Backing-Bean, sondern von einem zwischengespeicherten Wert aus einer ComponentStateHelper-Klasse übernommen.

Soweit ich verstehe, wird dies für das neue Feature von JSF 2 verwendet, um nur partielle Änderungen am Komponentenbaum zu speichern.

Was ich nicht verstehe, ist: Wie steht das mit der Validierungsphase? Warum gibt es einen Wert im Cache in der StateHelper Klasse für meine Checkbox, wenn die Validierung Fehler gefunden hat?

Antwort

4

Dies ist ein bekanntes Problem und wird ausführlich in this answer erläutert. Kurz gesagt, das Problem wird verursacht, weil die ungültigen Komponenten, die von <f:ajax render> gerendert werden sollen, aber nicht von ausgeführt wurden, zusammen mit dem ursprünglichen übermittelten Wert in einem ungültigen Zustand verbleiben. Wenn JSF die Eingabekomponente rendert, prüft JSF zuerst, ob der übermittelte Wert nicht null ist und zeigt sie dann an, andernfalls wird der Modellwert angezeigt. Sie müssen grundsätzlich den übergebenen Wert von Input-Komponenten zurücksetzen, die gerendert werden sollen, aber nicht von ajax ausgeführt werden.

Um dies zu erreichen, können Sie ein ActionListener das grundsätzlich führen Folgendes verwenden:

UIViewRoot viewRoot = context.getViewRoot(); 
PartialViewContext partialViewContext = facesContext.getPartialViewContext(); 
Set<EditableValueHolder> inputs = new HashSet<EditableValueHolder>(); 

// First find all to be rendered inputs and add them to the set. 
findAndAddEditableValueHolders(partialViewContext.getRenderIds(), inputs); 

// Then find all executed inputs and remove them from the set. 
findAndRemoveEditableValueHolders(partialViewContext.getExecuteIds(), inputs); 

// The set now contains inputs which are to be rendered, but which are not been executed. Reset them. 
for (EditableValueHolder input : inputs) { 
    input.resetValue(); 
} 

Dies wird als JSF issue 1060 und eine vollständige und wiederverwendbare Lösung wurde in der OmniFaces Bibliothek als ResetInputAjaxActionListener implementiert berichtet (Quellcode here und Vitrine Demo here).

+0

Vielen Dank für Ihre Antwort. Ich habe deine omififaces lib bereits in mein Projekt aufgenommen (für die 'FullAjaxExceptionHandlerFactory'). Es war also einfach, den 'ResetInputAjaxActionListener' in meine Datei' faces-config.xml' aufzunehmen. Aber dies wird nur in der 'InvokeApplicationPhase' aufgerufen, die nicht von meiner AJAX-Anfrage betroffen ist. Meine Ajax-Anfrage wird ausgelöst durch eine '' und dort durch ''. Aber ich werde mir das genauer ansehen. Ich habe deinen Vorschlag schnell ausprobiert. – Jens

+0

Es ist nicht getroffen? Bist du sicher? Möglicherweise müssen Sie den Server nach dem Bearbeiten von faces-config neu starten. – BalusC

+0

Ich habe es neu gestartet und ich habe die Quelle der Omnifaces hinzugefügt und einen Haltepunkt gesetzt, um zu sehen, ob es überhaupt getroffen wird. Es wird bei regulären Anfragen getroffen, aber nicht bei der Ajax-Anfrage, die durch die Auswahl der Checkbox ausgelöst wird. – Jens