39 votes

Restriction mutuelle des dates de début et de fin à l'aide de p:calendar (sans validation)

Nous avons besoin de présenter deux composants p:calendar à l'utilisateur, représentant chacun une date de début et de fin. Les deux dates comportent des dates, des heures et des minutes. PrimeFaces a parfaitement mindate , maxdate , minHour , maxHour , minMinute y minMinute attributs disponibles.

L'exigence est maintenant :

Il est impossible de donner à la date de début une valeur supérieure ou égale à la date de fin. Il est impossible de donner à la date de fin une valeur inférieure ou égale à la date de fin.

L'équation suivante devrait être vraie :

begin datetime < end datetime

Maintenant nous avons essayé le JSF suivant :

<p:calendar id="begin-date"
            value="#{debugManager.selectedBeginDate}"
            mindate="#{debugManager.minBeginDate}"
            maxdate="#{debugManager.maxBeginDate}"
            maxHour="#{debugManager.maxBeginHour}"
            maxMinute="#{debugManager.maxBeginMinute}"
            pattern="yyyy-MM-dd HH:mm"
            showButtonPanel="true"
            readonlyInput="true"
            navigator="true"
            showOn="button"
            required="true">
    <p:ajax event="dateSelect" update="end-date" />
</p:calendar>

<p:calendar id="end-date"
            value="#{debugManager.selectedEndDate}"
            mindate="#{debugManager.minEndDate}"
            minHour="#{debugManager.minEndHour}"
            minMinute="#{debugManager.minEndMinute}"
            pattern="yyyy-MM-dd HH:mm"
            showButtonPanel="true"
            readonlyInput="true"
            navigator="true"
            showOn="button">
    <p:ajax event="dateSelect" update="begin-date" />
</p:calendar>

Voici un exemple de méthode min/max (sans tenir compte de la date de fin) :

public Date getMinEndDate()
{
    return this.getSelectedBeginDate();
}

Comme vous pouvez le voir, la date de fin minimale est la date de début actuellement sélectionnée par AJAX. Le fait de définir une date de fin empêche de définir la date de début au-delà de la date de fin.

Les problèmes commencent lorsqu'on fait intervenir le temps dans l'équation...

Comme l'interface de p:calendar a des méthodes distinctes, le bean doit fournir la logique :

public int getMinEndHour()
{
    Date selectedBeginDate = this.getSelectedBeginDate();
    Date selectedEndDate = this.getSelectedEndDate();

    if ( selectedBeginDate != null && DateUtil.isSameDay( selectedBeginDate, selectedEndDate ) )
    {
        return DateUtil.getHourOf( selectedBeginDate );
    }

    return ComplianceConstants.DEFAULT_COMPLIANCE_CASE_MIN_END_HOUR;
}

En fait, cela ne s'applique que si une date de début a été fixée et si les dates de début et de fin sont actuellement les mêmes, restreindre l'heure de fin sélectionnable ( minHour de la date de fin) à l'heure de début.

Opérations :

Set the begin datetime to 2013-04-20 12:34 (legit)
Set the end   datetime to 2013-04-22 00:00 (legit)

Maintenant, l'heure de la date de fin est fixée à 00:00 et la sélection d'une date de calendrier 2013-04-20 devrait être autorisée tant que l'heure de fin est ajustée à au moins 12:35.

Le composant p:calendar ne peut cependant pas savoir cela et maintenant

sets the end datetime to 2013-04-20 00:00 (legit, but false)

...

Le problème est que lorsque l'utilisateur appuie sur une nouvelle date de fin dans le calendrier, les attributs mindate/maxdate ne peuvent pas empêcher l'utilisateur d'appuyer sur la même date que la date de début. Si la date de fin temps se trouve maintenant devant le même heure de début de la date on ne peut rien y faire (ce qui est faux).

Le problème suivant est que l'utilisateur peut fermer le calendrier et appuyer sur le bouton "Envoyer" pour insérer de fausses données dans la base de données. Bien sûr, un validateur pourrait/devrait être exécuté, mais nous devons trouver un moyen d'y parvenir sans validateur. .

Ce que nous essayions ensuite, c'était de corriger le setSelectedBeginDate( Date selectedBeginDate ) y setSelectedEndDate( Date selectedEndDate ) méthodes de réglage de l'ensemble java.util.Date les portions de temps si les dates étaient le même jour. Quelque chose comme ça :

public void adjustSelectedEndDate()
{
    if ( this.selectedEndDate != null )
    {
        this.log.infov( "adjustSelectedEndDate: b-hour = {0}, e-hour = {1}", DateUtil.getHourOf( this.selectedBeginDate ), DateUtil.getHourOf( this.selectedEndDate ) );

        if ( DateUtil.isSameDay( this.selectedBeginDate, this.selectedEndDate ) &&
            ( DateUtil.getHourOf( this.selectedEndDate ) < DateUtil.getHourOf( this.selectedBeginDate ) ) ||
              DateUtil.getHourOf( this.selectedEndDate ) == DateUtil.getHourOf( this.selectedBeginDate ) && DateUtil.getMinuteOf( this.selectedEndDate ) <= DateUtil.getMinuteOf( this.selectedBeginDate ) )
        {
            this.log.info( "Adjusting selected end date!" );

            this.selectedEndDate = DateUtil.addOneMinuteTo( DateUtil.copyTime( this.selectedBeginDate, this.selectedEndDate ) );
        }
    }
}

Cela nous a obligés à ajouter @this au update de chaque p:calendar de sorte que les récupérateurs respectifs ( getSelectedBeginDate() y getSelectedEndDate + les limiteurs min/max) seront appelés lors de la mise à jour.

Placer un @this sur la mise à jour confond toutefois les composants p:calendar, rendant les curseurs de temps glissants une seule fois. Les événements de glissement ultérieurs sont tout simplement ignorés, et le comportement est cassé.

Q's

  • Comment abordez-vous généralement la résolution de ce problème ?
  • Est-ce que l'on utilise p:remoteCommand le moyen d'obtenir ce que nous voulons ?

Facultatif Q :

  • Pourquoi le p:calendar de PrimeFaces n'a-t-il pas été implémenté pour fournir un minDateTime et un maxDateTime uniques, ce qui pourrait potentiellement résoudre les problèmes en question ?

Je parie que le scénario que j'ai décrit a déjà été résolu auparavant. J'apprécierais beaucoup si vous pouviez décrire l'approche que vous avez adoptée pour le résoudre (ou même partager une solution partielle).

1voto

JustinC Points 356

Préface :

Je ne travaille pas avec JSF, mais il y a quelques éléments qui pourraient vous ramener là où vous voulez être :

a) quand travailler avec la seule partie date d'un dateTime dans un calendrier standard envisagez de l'utiliser :

someCalendar.set(Calendar.MILLISECOND, 0)

b) envisager d'utiliser joda-time comme cela semble être fréquemment recommandé ( aquí , aquí et de nombreux autres endroits ) par rapport à la bibliothèque standard en termes de correction, de performances et de facilité d'utilisation dans de nombreuses situations.

c) Assurez-vous que le champ d'application de votre bean survit à chaque appel ajax (pas de redirection, seulement l'envoi de post-backs standard, etc.) et que chaque gestionnaire d'événement reçoit le contexte des faces (ex. FacesContext facesContext = FacesContext.getCurrentInstance(); )

d) mindate et autres ne fonctionnent probablement pas comme vous le pensez, et je ne m'attends pas à ce qu'un comportement automatique puisse être interverti aussi facilement.

Lorsque ces options ne sont pas disponibles, et que vous devez tout faire vous-même avec ce que vous avez :

Philisophique / UX : La première chose que je ferais est de supprimer l'attente d'un arrangement ou d'une perspective de la paire de dates. Ne traitez pas la paire comme un vecteur qui expose ou attend une direction sur la ligne de temps.

  • En d'autres termes, est-ce qu'un start o from toujours inférieure ou antérieure à une date end o to date ? Non, comme on peut le voir pour une requête de données historiques, ou pour appliquer des corrections à des événements qui ne se sont pas encore produits ou qui se sont déjà produits ?

    Cette connotation peut facilement désorienter l'utilisateur qui ne sait pas s'il va "en arrière" ou "en avant de" (et peut facilement s'embrouiller lui-même). Au lieu de cela, je traiterais une paire de dates avec une période de temps entre elles comme étant juste et simplement cela a pair of dates ou un range ou un period qui déclare un intervalle, et déduisent leur position relative sur la ligne de temps en fonction des valeurs choisies en conséquence. De cette façon, vous pouvez respecter les exigences respectives et inhérentes selon lesquelles les dates ne sont jamais égales, et la gauche est toujours à gauche, la droite toujours à droite.

Nous ne pouvons pas déduire ce que signifie "commencer" ou "de", mais nous pouvons déduire une signification et une relation relative : une droite, une gauche et un entre sur une ligne de temps chronologique. Note : Toujours convertir les dates en UTC avant d'effectuer un calcul ou une comparaison.

long oneDateValue = oneDate.toUtc().toMilliseconds();
long anotherDateValue = anotherDate.toUtc().toMilliseconds();

long right = max (oneDateValue, anotherDateValue);
long left = min (oneDateValue, anotherDateValue);

Évaluer la précision : La deuxième chose à laquelle je ferais attention lorsque vous travaillez avec une plage de dates dans n'importe quel langage est similaire à la façon dont vous pouvez traiter les nombres à virgule flottante. Pour les comparaisons, ne cherchez pas l'égalité, mais comparez plutôt le delta à un "niveau d'erreur acceptable". En d'autres termes, l'application n'est réellement concernée que par un certain degré de précision, alors assurez-vous que seule cette précision est capturée et prise en compte :

const int dateTimeResolutionInMs = 86400000; // milliseconds per day

public bool areEssentiallySame(long left, long right) {

   // the difference between right and left is less than our precision 
   // requires, thus dates are effectively the same
   return (right - left < dateTimeResolutionInMs);
}

La précision de la coercition : Troisièmement, comment résoudre la différence de valeurs, même si elle se situe dans la plage de résolution ? (On a donné à notre application une précision supérieure à ce qu'elle peut gérer, à ce qu'elle attend ou à ce dont elle a besoin).

long diff = value % dateTimeResolutionInMs;

  1. Coupé : return value - diff;

  2. Le plus proche (w/bias) : return value + (diff < dateTimeResolutionInMs/ 2) ? -1 * diff : dateTimeResolutionInMs - diff;

  3. Autres : il existe de nombreuses autres stratégies permettant de réduire ou d'augmenter une valeur en fonction de la résolution ou de la précision souhaitée.

Addendum : En ce qui concerne l'obtention de retours/appels Jax pour retourner une vue avec les valeurs que vous attendez pour les événements déclenchés par une calendar vous pouvez séparer cette préoccupation dans une nouvelle question si la note dans la préface ne vous a pas mené à quelque chose, et vous avoir la certitude que votre haricot est correctement enregistré et reconnu. Il se peut que des problèmes spécifiques au navigateur ou à la version du navigateur contribuent à ce comportement indésirable. Comme pour toute chose, il existe des problèmes, connus ou non.

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