Primefaces calendar with input mask

Goal

To have a primefaces calendar with an input mask

Description

In one of my projects, using Primefaces (3.5), there was the need to create a calendar component with an input mask so that it was possible for the user to introduce the date in a more or less controlled way, according to the pattern. However, and although Primefaces already provides us with a wide variety of components, I could not find one that could directly satisfy this need. This recipe explains the solution I implemented to fulfill the need.

How to

Use the component inputMask with a datepicker associated and to make the solution pretty, lets implement as a Facelets component, as such (the implementation is based in using a inputMask component with a given styleClass and the inclusion of a bit of JQuery that associates a datepicker to any element containing that specific class whenever the document is ready, i.e., after the initial page load and after any AJAX request being completed, due to the fact that an AJAX request may potentially refresh the rendered page, including new components that would need to be presented as an input masked calendar). In order to use the next snippet as a component, create a file named calendar.xhtml, for instance, inside src/main/resources/META-INF/linkare, if using Maven or the equivalent so that you will be able to refer to the component’s namespaceas linkare and the component name calendar:

<!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://java.sun.com/jsf/html"
    xmlns:composite="http://java.sun.com/jsf/composite"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<composite:interface>
    <composite:attribute name="value" type="java.util.Date" required="true"
        default="true" shortDescription="The value of this component" />
    <composite:attribute name="mask" type="java.lang.String"
        required="false" default="9999-99-99"
        shortDescription="The mask of the input component. The default value is 9999-99-99" />
    <composite:attribute name="pattern" type="java.lang.String"
        required="false" default="yyyy-MM-dd"
        shortDescription="The date pattern for the component. It should match the mask pattern. 
        The default value is yyyy-MM-dd" />
    <!-- Other (optional) attributes of the inputMask component may be specified next -->
    ...
</composite:interface>
<composite:implementation>
    <!-- This component has been simplified but it could include any attribute of the inputMask 
         component that we wish our developer clients to be able to specify -->
    <p:inputMask id="calendar" styleClass="masked_calendar"
        mask="#{cc.attrs.mask}" value="#{cc.attrs.value}">
        <f:convertDateTime pattern="#{cc.attrs.pattern}"
            timeZone="Europe/Lisbon" locale="pt" />
    </p:inputMask>
    <h:outputScript target="head" library="calendar" name="calendar.js" />
</composite:implementation>
</html>

The javascript that associates the datepicker to the component is shown next (included anytime our facelet component is included – in the previous snipped, we are using JSF resource handlers to include our Javascript file):

var Calendar = (function() {
    return {
        maskedCalendar : function() {
            $('.masked_calendar').datepicker({
                showOn: 'button',
                dateFormat: 'yy-mm-dd',
                changeMonth: true,
                changeYear: true,
                showAnim: "slide",
                closeText : 'Fechar',
                prevText : 'Anterior',
                nextText : 'Seguinte',
                currentText : 'Hoje',
                monthNames : [ 'Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho',
                                'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro' ],
                monthNamesShort : [ 'Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago',
                                'Set', 'Out', 'Nov', 'Dez' ],
                dayNames : [ 'Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta',
                                'Sábado' ],
                dayNamesShort : [ 'Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb' ],
                dayNamesMin: [ "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab" ],
                weekHeader : 'Semana',
                firstDay : 1,
                isRTL : false,
                showMonthAfterYear : false,
                yearSuffix : '',
                timeOnlyTitle : 'Só Horas',
                timeText : 'Tempo',
                hourText : 'Hora',
                minuteText : 'Minuto',
                secondText : 'Segundo',
                ampm : false,
                month : 'Mês',
                week : 'Semana',
                day : 'Dia',
                allDayText : 'Dia completo',
                duration: "normal"
            });
        }
    };
})();

$(document).ready(function() {
    Calendar.maskedCalendar();
});

$(document).ajaxComplete(function() {
    $('.masked_calendar').ready(function() {
        Calendar.maskedCalendar();
    });
});

And finally, the client may use the component as follows (notice how we refer to the input masked calendar’s component inside our composite facelet component through a relative name starting in the name we set to our component lnk:calendar, i.e., myDate:calendar):

<?xml version="1.0" encoding="ISO-8859-15"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:lnk="http://java.sun.com/jsf/composite/linkare"
                xmlns:p="http://primefaces.org/ui"
                xmlns:pe="http://primefaces.org/ui/extensions"
                xmlns:c="http://java.sun.com/jsp/jstl/core">
....
<p:outputLabel for="myDate:calendar" value="My date:" />
<lnk:calendar id="myDate" value="#{myBean.myDate}"/>
<p:message for="myDate:calendar" />
</ui:composition>

Explanations

Basically, this solution implements a wrapper around the inputMask component that already provides the user with a “safe” way to introduce input in a given pattern with a date picker so that it seems like a ready-made calendar component with input pattern capabilities.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s