2011-01-03 14 views
1

Wir haben kürzlich unsere Systeme von JSF 1.2 auf JSF 2.0 aktualisiert und sind dabei, alles zum Laufen zu bringen. Es treten jedoch Probleme mit Validierern auf, wenn sie in Datentabellen oder ähnlichen Komponenten verwendet werden. Das Problem besteht darin, dass der Validierer die von der Datentabelle festgelegte Variable nicht verwenden kann.JSF 2-Validatoren können nicht "var" von dataTable verwenden

Hier ist ein Beispiel:

VALIDATOR:

package test; 

import javax.faces.application.FacesMessage; 
import javax.faces.component.UIComponent; 
import javax.faces.context.FacesContext; 
import javax.faces.validator.FacesValidator; 
import javax.faces.validator.Validator; 
import javax.faces.validator.ValidatorException; 

@FacesValidator("test.TestValidator") 
public class TestValidator implements Validator { 

    private Integer length; 

    public TestValidator() { 
     super(); 
    } 

    @Override 
    public void validate(FacesContext context, 
      UIComponent component, Object value) throws ValidatorException { 

     String text = (String) value; 
     if (text == null || text.trim().length() == 0) { 
      return; 
     } 

     if (length != null && text != null && length.intValue() < text.length()) { 
      String message = "The text is too long. It was " + text.length() + 
        ", but only " + length + " characters are allowed."; 
      FacesMessage fm = new FacesMessage(
        FacesMessage.SEVERITY_ERROR, message, null); 
      throw new ValidatorException(fm); 
     } 
    } 

    public Integer getLength() { 
     return length; 
    } 

    public void setLength(Integer length) { 
     this.length = length; 
    } 
} 

TagLib:

<?xml version="1.0" encoding="UTF-8"?> 
    <facelet-taglib 
     xmlns="http://java.sun.com/xml/ns/javaee" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd" 
     version="2.0"> 
     <namespace>http://industry-supply.dk/test</namespace> 
     <tag> 
      <tag-name>testValidator</tag-name> 
      <validator> 
       <validator-id>test.TestValidator</validator-id> 
      </validator> 
      <attribute> 
       <name>length</name> 
       <required>true</required> 
       <type>java.lang.Integer</type> 
      </attribute> 
     </tag> 
    </facelet-taglib> 

MANAGED BEAN:

package test; 

import javax.faces.bean.ManagedBean; 
import javax.faces.bean.RequestScoped; 

@ManagedBean(name = "testBean") 
@RequestScoped 
public class TestBean { 

    public TestBean() { 
    } 

    public String[] getKey() { 
     return new String[]{ 
        "0", 
        "1", 
        "2", 
        "3" 
       }; 
    } 

    public String[] getValue() { 
     return new String[]{ 
        "This is a text and it's too long.", 
        "This is a text and it's too long.", 
        "This is a text and it's too long.", 
        "This is a text and it's too long." 
       }; 
    } 
} 

JSF:

Beim Ausführen des Projekts werden 4 Eingabefelder und eine Befehlsschaltfläche angezeigt. Jedes Eingabefeld enthält einen 33 Zeichen langen Text. Wenn Sie auf "Senden" klicken, wird die Fehlermeldung "Der Text ist zu lang. Es war 33, aber nur 0 Zeichen sind erlaubt." wird für jede Zeile/Feld angezeigt. Das ist falsch, weil "test: testValidator length =" # {testBean.key [k]} "" die Länge auf 0 für die erste Zeile, 1 für die zweite Zeile, 2 für die dritte Zeile und 3 für die vierte Zeile angibt. Für die letzte Zeile sollte die Fehlermeldung also lauten: "Der Text ist zu lang. Er war 33, aber es sind nur 3 Zeichen erlaubt."

Das Problem ist, dass der Validator scheint nicht Zugriff auf die k-Variable aus der DataTable-Komponente in der JSF-Datei. Dies funktionierte in JSF 1.2, aber wir können es nicht in JSF 2.0 zum Laufen bringen. Wir haben jetzt Tage mit dem Problem verbracht und brauchen wirklich Hilfe. Ideen jemand?

+0

Ich fühle mich vielleicht sollte ich das Problem ein bisschen mehr angeben. Es scheint, dass die Länge des Validators mit der Ganzzahl festgelegt wird, die zu Beginn der Anfrage aus dem Ausdruck # {testBean.key [k]} aufgelöst wird, aber später in der Anfrage, wenn die dataTable iteriert, wird der Ausdruck nicht noch einmal aufgelöst Länge neu eingestellt. Da k nur während der dataTables-Iterationen festgelegt wird, bedeutet dies, dass die Länge während der gesamten Anforderung null bleibt. Die perfekte Lösung wäre, wenn der Ausdruck einmal pro Iteration aufgelöst werden könnte, aber ich weiß nicht, wie man dies erzwingt. –

+0

Ich habe diesen Kommentar nicht gesehen, als ich meine Antwort geschrieben habe, aber sehe meine Bemerkungen über 'ValueExpressions'. Sie müssen den Ausdruck im Validator auswerten (Sie können den 'ELContext' aus dem' FacesContext' erhalten). – McDowell

Antwort

0

Ich glaube, dass Ihr Validator einen Fehler enthält und Sie hatten gerade Glück gehabt, wenn Sie vorher implementiert haben.

Vom documentation:

... wenn die Validator Klasse wünscht mit der Ansicht Konfiguration Eigenschaftswerte gespeichert und wiederhergestellt haben, muss die Umsetzung auch StateHolder implementieren.

Wenn die Ansicht während des POST-Vorgangs wiederhergestellt wird, ist der Längenstatus möglicherweise nicht mehr vorhanden.

Ich erinnere mich nicht, was mit ValueExpressions auf Validatoren geschieht - im Gegensatz zu Komponenten, neigt Validatoren nicht binding maps haben sie in zu halten, Sie müssen Ihre eigenen ValidatorHandler oder etwas schaffen -. Ich habe nicht in diesem Teil angesehen der API im Detail.

+0

Ich habe versucht, StateHolder und sogar PartialStateHolder zu implementieren. Es scheint keinen Unterschied zu machen. Aus meiner Sicht ist das Problem, dass wir von JSP zu Facelets wechseln und unsere Komponenten, Konverter und Validatoren ändern, sodass sie keine Tag-Datei mehr haben. Zuvor hatten wir eine Tag-Datei, die ValueExpressions verwendete und die Komponente festlegte. Aber mit Facelets haben wir die Tag-Datei nicht mehr. Stattdessen wird die Komponente/Converter/Validator direkt festgelegt und mit dem aufgelösten Ausdruck festgelegt. Nicht der Ausdruck selbst. Dies bedeutet, dass wir den Ausdruck nicht erneut auflösen können. –

+0

Ich denke, ich werde versuchen, mit einem ValidatorHandler zu arbeiten, wie Sie vorschlagen. Ich werde mit den Ergebnissen posten, bin aber offen für weitere Vorschläge. –

+0

@ Bjørn Stenfeldt - vielen Dank für Ihre Lösung - interessant. Ich würde StateHolder immer noch gemäß dem Klassenvertrag implementieren. Ich vermute, dass Ihre Ansichten im Session-RAM gehalten werden. Wenn Sie jemals den Mechanismus zum Speichern von Status gewechselt haben, könnte Ihre Klasse brechen. Dies ist jedoch kein Risiko für Sie. – McDowell

1

In Ordnung, es scheint alles funktioniert jetzt. Ich habe einen benutzerdefinierten ValidatorHandler erstellt, wie von McDowell vorgeschlagen.Hier sind meine Änderungen:

TagLib:

<handler-class>test.CustomValidatorHandler</handler-class> 

VALIDATOR:

package test; 

import javax.el.ValueExpression; 
import javax.faces.application.FacesMessage; 
import javax.faces.component.UIComponent; 
import javax.faces.context.FacesContext; 
import javax.faces.validator.FacesValidator; 
import javax.faces.validator.Validator; 
import javax.faces.validator.ValidatorException; 

@FacesValidator("test.TestValidator") 
public class TestValidator implements Validator { 

    public TestValidator() { 
     super(); 
    } 

    @Override 
    public void validate(FacesContext context, 
      UIComponent component, Object value) throws ValidatorException { 

     String text = (String) value; 
     if (text == null || text.trim().length() == 0) { 
      return; 
     } 

     Integer length = getLengthValue(); 

     if (length != null && text != null && length.intValue() < text.length()) { 
      String message = "The text is too long. It was " + text.length() + 
        ", but only " + length + " characters are allowed."; 
      FacesMessage fm = new FacesMessage(
        FacesMessage.SEVERITY_ERROR, message, null); 
      throw new ValidatorException(fm); 
     } 
    } 

    private ValueExpression lengthExpression; 

    public ValueExpression getLength() { 
     return lengthExpression; 
    } 

    public void setLength(ValueExpression length) { 
     this.lengthExpression = length; 
    } 

    public Integer getLengthValue() { 
     Integer length = null; 
     if (lengthExpression != null) { 
      length = (Integer) lengthExpression.getValue(
        FacesContext.getCurrentInstance().getELContext()); 
     } 
     return length; 
    } 
} 

VALIDATOR HANDLER:

package test; 

import javax.el.ValueExpression; 
import javax.faces.context.FacesContext; 
import javax.faces.view.facelets.FaceletContext; 
import javax.faces.view.facelets.ValidatorConfig; 
import javax.faces.view.facelets.ValidatorHandler; 

public class CustomValidatorHandler extends ValidatorHandler { 

    private ValueExpression lengthExpression; 

    public CustomValidatorHandler(ValidatorConfig config) { 
     super(config); 
     FacesContext facesContext = FacesContext.getCurrentInstance(); 
     FaceletContext faceletContext = (FaceletContext) facesContext.getAttributes().get(FaceletContext.FACELET_CONTEXT_KEY); 
     lengthExpression = config.getTag().getAttributes().get("length").getValueExpression(faceletContext, Integer.class); 
    } 

    @Override 
    public void setAttributes(FaceletContext ctx, Object instance) { 
//  super.setAttributes(ctx, instance); 
     if (instance instanceof TestValidator) { 
      TestValidator validator = (TestValidator) instance; 
      validator.setLength(lengthExpression); 
     } 
    } 
} 
Verwandte Themen