14 votes

Comment définir les propriétés du convertisseur pour chaque ligne/élément de h:dataTable/ui:repeat ?

J'ai créé une date et une heure ISO personnalisées Converter :

public class IsoDateTimeConverter implements Converter, StateHolder {

    private Class type;
    private String pattern;

    private boolean transientValue = false;

    public void setType(Class type) {
        this.type = type;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
        if (StringCheck.isNullOrEmpty(value)) {
            throw new ConverterException("value not specified");
        }

        try {
            if (IsoDate.class.equals(type)) {

                if (WebConst.ISO_DATE_NONE.equals(value)) {
                    return IsoDate.DUMMY;
                } else {
                    //TODO User spezifische TimeZone auslesen
                    return new IsoDate(value, TimeZone.getDefault().getID());
                }

            } else if (IsoTime.class.equals(type)) {

                if (WebConst.ISO_TIME_NONE.equals(value)) {
                    return IsoTime.DUMMY;
                } else {
                    //TODO User spezifische TimeZone auslesen
                    return new IsoTime(value, TimeZone.getDefault().getID());
                }

            } else if (IsoTimestamp.class.equals(type)) {

                if (WebConst.ISO_TIMESTAMP_NONE.equals(value)) {
                    return IsoTimestamp.DUMMY;
                } else {
                    //TODO User spezifische TimeZone auslesen
                    return new IsoTimestamp(value, TimeZone.getDefault().getID());
                }

            } else {
                throw new ConverterException("value not convertible");
            }
        } catch (Exception e) {
            throw new ConverterException(e.getMessage());
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException {
        if (value == null) {
            throw new ConverterException("value not specified");
        }

        if (IsoDate.class.equals(value)) {
            IsoDate isoDate = (IsoDate) value;

            if (isoDate.isDummy()) {
                return WebConst.ISO_DATE_NONE;
            } else {
                //TODO User spezifische TimeZone auslesen
                return isoDate.toString(pattern, TimeZone.getDefault().getID(), false);
            }

        } else if (IsoTime.class.equals(value)) {
            IsoTime isoTime = (IsoTime) value;

            if (isoTime.isDummy()) {
                return WebConst.ISO_TIME_NONE;
            } else {
                //TODO User spezifische TimeZone auslesen
                return isoTime.toString(pattern, TimeZone.getDefault().getID(), false);
            }

        } else if (IsoTimestamp.class.equals(value)) {
            IsoTimestamp isoTimestamp = (IsoTimestamp) value;

            if (isoTimestamp.isDummy()) {
                return WebConst.ISO_TIMESTAMP_NONE;
            } else {
                //TODO User spezifische TimeZone auslesen
                return isoTimestamp.toString(pattern, TimeZone.getDefault().getID(), false);
            }

        } else {
            throw new ConverterException("value not convertible");
        }
    }

    @Override
    public Object saveState(FacesContext context) {
        return new Object[]{type, pattern};
    }

    @Override
    public void restoreState(FacesContext context, Object state) {
        type = (Class) ((Object[]) state)[0];
        pattern = (String) ((Object[]) state)[1];
    }

    @Override
    public boolean isTransient() {
        return transientValue;
    }

    @Override
    public void setTransient(boolean transientValue) {
        this.transientValue = transientValue;
    }
}

Et j'utilise le Converter como <mh:IsoDateTimeConverter> dans la vue suivante :

<p:dataTable value="#{imports.list}" var="item">
    <p:column>
        <h:outputText value="#{item.balanceDate}" immediate="true">
            <mh:IsoDateTimeConverter type="#{webConst.ISO_DATE_CLASS}" pattern="#{webConst.ISO_DATE_FORMAT}"/>
        </h:outputText>
    </p:column>
</p:dataTable>

Le problème, c'est que lorsque j'ouvre cette vue pour la première fois, toutes les propriétés sont définies dans mon fichier Converter une seule fois, puis la table de données rend et convertit les valeurs en fonction des propriétés initiales.

Je m'attendais à ce que les propriétés soient définies pour chaque ligne. Comment puis-je obtenir cela ?

26voto

BalusC Points 498232

En fait, vous vous attendiez à ce que les propriétés du convertisseur soient définies chaque fois qu'une ligne de tableau de données est rendue. Ce n'est effectivement pas le cas. JSF ne créera qu'une seule instance de convertisseur par composant lorsque la vue doit être construite, il ne créera pas/réinitialisera le convertisseur chaque fois que la ligne est rendue.

Il y a plusieurs façons de le faire fonctionner.

  • Passez les attributs dynamiques comme <f:attribute> du composant et laisser le Converter intercepter sur cela. Vous pouvez trouver un exemple ici : JSF convertDateTime avec le fuseau horaire dans les tables de données . On peut alors l'utiliser comme suit

    <h:outputText value="#{item.balanceDate}">
        <f:converter converterId="isoDateTimeConverter" />
        <f:attribute name="pattern" value="#{item.pattern}" />
    </h:outputText>

  • Utilisez une fonction EL au lieu d'un Converter . Vous pouvez trouver un exemple ici : Facelets et JSTL (Conversion d'une date en une chaîne de caractères à utiliser dans un champ) . On peut alors l'utiliser comme suit

    <h:outputText value="#{mh:convertIsoDate(item.balanceDate, item.pattern)}" />

  • Lier le convertisseur et les données de la table de données. DataModel comme une propriété du même managed bean. De cette façon, vous pourrez définir les propriétés du convertisseur en fonction des données de la ligne avant de les renvoyer. Voici un exemple de lancement de base basé sur des composants JSF standard et des objets gérés standard DateTimeConverter (il devrait fonctionner aussi bien sur les composants PrimeFaces qu'avec votre convertisseur personnalisé) :

    <h:dataTable value="#{bean.model}" var="item">
        <h:column>
            <h:outputText value="#{item.date}" converter="#{bean.converter}" />
        </h:column>
    </h:dataTable>

    avec

    @ManagedBean
    @ViewScoped
    public class Bean implements Serializable {
    
        private List<Item> items;
        private DataModel<Item> model;
        private DateTimeConverter converter;
    
        @PostConstruct
        public void init() {
            items = Arrays.asList(
                new Item(new Date(), "dd-MM-yyyy"), 
                new Item(new Date(), "yyyy-MM-dd"), 
                new Item(new Date(), "MM/dd/yyyy"));
            model = new ListDataModel<Item>(items);
            converter = new DateTimeConverter();
        }
    
        public DataModel<Item> getModel() {
            return model;
        }
    
        public Converter getConverter() {
            converter.setPattern(model.getRowData().getPattern());
            return converter;
        }
    
    }

    (le Item est juste un haricot avec deux propriétés Date date y String pattern )

    il en résulte

    23-09-2011
    2011-09-23
    09/23/2011


  • Utilice OmniFaces <o:converter> à la place. Il prend en charge l'évaluation en temps de rendu des EL dans les attributs. Voir aussi el <o:converter> exemple de vitrine .

    <h:outputText value="#{item.balanceDate}">
        <o:converter converterId="isoDateTimeConverter" pattern="#{item.pattern}" />
    </h:outputText>

1voto

Kipperfer Points 78

L'excellente (comme toujours) réponse de BalusC ci-dessus est complète mais ne répond pas exactement à mes besoins. Dans mon cas, j'ai besoin de lier un fichier Converter à chaque itération dans un ui:repeat . J'ai besoin d'un autre Converter en fonction de la répétition de chaque élément. La réponse m'a cependant mis sur la bonne voie, et j'ai donc pensé qu'il valait la peine de partager ma solution au cas où elle aiderait quelqu'un d'autre.

J'utilise un Converter qui délègue tout son travail à un autre Converter objet spécifié dans l'attribut, comme dans la première des réponses de BalusC. Notez que cela n'est d'aucune utilité si vous souhaitez utiliser des convertisseurs avec des paramètres. Converter à une propriété d'un objet répétitif.

Voici la délégation Converter . C'est aussi un Validator qui fonctionne exactement de la même manière.

// package and imports omitted for brevity
@FacesConverter(value="delegatingConverter")
@FacesValidator(value="delegatingValidator")
public class Delegator implements Converter, Validator {

    // Constants ---------------------------------------------------------------
    private static final String CONVERTER_ATTRIBUTE_NAME = "delegateConverter";
    private static final String VALIDATOR_ATTRIBUTE_NAME = "delegateValidator";

    // Business Methods --------------------------------------------------------
    @Override
    public Object getAsObject(FacesContext context, UIComponent component, 
            String value) throws ConverterException {
        return retrieveDelegate(component, Converter.class, CONVERTER_ATTRIBUTE_NAME)
                .getAsObject(context, component, value);
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, 
            Object value) throws ConverterException {
        return retrieveDelegate(component, Converter.class, CONVERTER_ATTRIBUTE_NAME)
                .getAsString(context, component, value);
    }

    @Override
    public void validate(FacesContext context, UIComponent component, 
            Object value) throws ValidatorException {
        retrieveDelegate(component, Validator.class, VALIDATOR_ATTRIBUTE_NAME)
                .validate(context, component, value);
    }

    private <T> T retrieveDelegate(UIComponent component, Class<T> clazz,
            String attributeName) {
        Object delegate = component.getAttributes().get(attributeName);
        if (delegate == null) {
            throw new UnsupportedOperationException("No delegate was specified."
                    + "  To specify, use an f:attribute tag with: name=\"" 
                    + attributeName + "\"");
        }
        if (!(clazz.isAssignableFrom(delegate.getClass()))) {
            throw new UnsupportedOperationException("The specified delegate "
                    + "was not a " + clazz.getSimpleName() + " object.  " +
                    "Delegate was: " + delegate.getClass().getName());
        }
        return (T) delegate;
    }
}

Donc maintenant où je voudrais utiliser ce code dans mon ui:repeat, ce qui ne fonctionnera pas :

<h:outputText value="#{item.balanceDate}">
    <f:converter binding="#{item.converter} />
    <f:validator binding="#{item.validator} />
</h:outputText>

Je peux à la place utiliser ce code, qui fonctionne bien :

<h:outputText value="#{item.balanceDate}">
  <f:converter converterId="delegatingConverter"/>
  <f:validator validatorId="delegatingValidator"/>
  <f:attribute name="delegateConverter" value="#{item.converter}"/>
  <f:attribute name="delegateValidator" value="#{item.validator}"/>
</h:outputText>

En supposant que l'élément répétitif a une valeur de public Converter getConverter() et similaire pour la méthode Validator .

Cela présente l'avantage que les mêmes Converter ou Validator qui sont utilisés ailleurs peuvent être réutilisés sans aucune modification.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X