Thursday, August 25, 2011

Combine jQuery Datepicker and Spinner for fast day incrementation / decrementation

Is it possible to add + / - buttons to jQuery UI Datepicker in order to increment / decrement days in a comfortable way? Yes, it's possible. I have done this task - see screenshot.


This is an extended PrimeFaces Calendar with added "dateSpinner" mode. I would like to omit JSF part and only show HTML / CSS / JavaScript part of such combined calendar. All what you need is to wrap the datepicker markup with a span element, add themable up / down arrows and use datepicker utility functions $.datepicker.formatDate(format, date, settings) and $.datepicker.parseDate(format, value, settings).

HTML
Original HTML with a calendar icon (called icon trigger in jQuery Datepicker) looks very simple
 
<input id="datepicker" class="hasDatepicker" type="text">
<img class="ui-datepicker-trigger" src="images/calendar.gif" alt="..." title="...">
 
You have to extend this as follows
<span id="datepickerWrapper" class="ui-ccalendar ui-widget ui-corner-all">
	<input id="datepicker" class="hasDatepicker" type="text">
	<img class="ui-datepicker-trigger" src="images/calendar.gif" alt="..." title="...">
	<a class="ui-ccalendar-button ui-ccalendar-up ui-corner-tr ui-button ui-widget ui-state-default ui-button-text-only">
		<span class="ui-button-text">
			<span class="ui-icon ui-icon-triangle-1-n"></span>
		</span>
	</a>
	<a class="ui-ccalendar-button ui-ccalendar-down ui-corner-br ui-button ui-widget ui-state-default ui-button-text-only">
		<span class="ui-button-text">
			<span class="ui-icon ui-icon-triangle-1-s"></span>
		</span>
	</a>
</span>

CSS
The most CSS classes above are jQuery UI classes. I marked own classes with ui-ccalendar. In the CSS part is very important to shift spinner's arrow buttons to left. I have shifted absolute positioned buttons with CSS statement right: 17px;. We achieve with this displacement that the calendar icon is visible.
.ui-ccalendar {
    display: inline-block;
    overflow: visible;
    padding: 0;
    position: relative;
    vertical-align: middle;
}

.ui-ccalendar-button {
    cursor: default;
    display: block;
    font-size: 0.5em;
    height: 50%;
    margin: 0;
    overflow: hidden;
    padding: 0;
    position: absolute;
    right: 17px;
    text-align: center;
    vertical-align: middle;
    width: 16px;
    z-index: 100;
}

.ui-ccalendar .ui-icon {
    left: 0;
    margin-top: -8px;
    position: absolute;
    top: 50%;
}

.ui-ccalendar-up {
    top: 0;
}

.ui-ccalendar-down {
    bottom: 0;
}

.ui-ccalendar .ui-icon-triangle-1-s {
    background-position: -65px -16px;
}

.ui-ccalendar .ui-icon-triangle-1-n {
    margin-top: -9px;
}

JavaScript
In this part I use the mentioned above utility functions formatDate() / parseDate() and setDate() / getDate() API of Date object to increment / decrement a single day. Month and year boundaries are considered automatically and in- / decremented if necessary. In- / decrementation logic is bound to mousedown event on spinner buttons.
var datepickerInput = $('#datepicker');
datepickerInput.datepicker({dateFormat: 'yy-mm-dd', ... other configuration if needed ...});

$('#datepickerWrapper').children('.ui-ccalendar-button').mouseover(function() {
	$(this).addClass('ui-state-hover');
}).mouseout(function() {
	$(this).removeClass('ui-state-hover');
}).mouseup(function() {
	$(this).removeClass('ui-state-active');
}).mousedown(function() {
	var el = $(this);
	el.addClass('ui-state-active');
	try {
		// get configured date format
		var dateFormat = datepickerInput.datepicker("option", "dateFormat");

		// extract a date from a string value with a specified format
		var date = $.datepicker.parseDate(dateFormat, datepickerInput.val());
		if (el.hasClass('ui-ccalendar-up')) {
			// increment day
			date.setDate(date.getDate() + 1);
		} else {
			// decrement day
			date.setDate(date.getDate() - 1);
		}

		// format a date into a string value with a specified format
		var strDate = $.datepicker.formatDate(dateFormat, date);
		datepickerInput.val(strDate);
	} catch (err) {
		// ignore and nothing to do
	}
});
Important is here "dateFormat" option. Date format normally depends on user locale and should be passed from outside.

P.S. Just now I found a desktop example of such combined calendar too. So, you see, it's an useful and established widget :-).

Monday, August 22, 2011

GWT composite widgets vs. JSF composite components

I have started to learn GWT (Google Web Toolkit). I don't want to compare all features of both frameworks. I would like to pick just one feature. In GWT you can write autonomous and reusable composite widgets and apply them many times on one or several pages. GWT following say that's not really possible in JSF 2 because one composite component is normally linked with a managed bean. If you use the same composite component twice and change value(s) in one composite component, changed value(s) will be propogated through the bean to another component too. Is it really true?

Take an example. Assume, we have to write a composite widget / component consist of a label, an input field and a button which are aligned horizontally. The GWT solution could be looked like this one
public class CompositeExample implements EntryPoint
{
	private static class LabeledInput extends Composite {
		private Label label = new Label();
		private TextBox textBox = new TextBox();
		private Button button = new Button("Jump!", new ClickHandler() {
			public void onClick(ClickEvent event) {
				// do something
			}
		});

		public LabeledInput() {
			HorizontalPanel panel = new HorizontalPanel();
			panel.add(label);
			panel.add(textBox);
			panel.add(button);

			initWidget(panel);
		}
	}

	public void onModuleLoad() {
		LabeledInput labeledInput = new LabeledInput();
		RootPanel.get().add(labeledInput);
	}
}
I defined a class named LabeledInput which extends the class com.google.gwt.user.client.ui.Composite. The new composite widget LabeledInput is well reusable many times on any pages. No problems here. A corresponding JSF solution could be implemented in a file labeledInput.xhtml as follows
<html xmlns="http://www.w3.org/1999/xhtml"
	  xmlns:h="http://java.sun.com/jsf/html"
	  xmlns:cc="http://java.sun.com/jsf/composite">
<cc:interface>
	<cc:attribute name="model" required="true">
		<cc:attribute name="labelValue" required="true"/>
		<cc:attribute name="inputValue" required="true"/>
		<cc:attribute name="jump" required="true" method-signature="void f(javax.faces.event.ActionEvent)"/>
	</cc:attribute>
	<cc:actionSource name="jump"/>
</cc:interface>
<cc:implementation>
	<h:panelGrid columns="3">
		<h:outputLabel for="#{cc.clientId}:input" value="#{cc.attrs.model.labelValue}"/>
		<h:inputText id="input" value="#{cc.attrs.model.inputValue}"/>
		<h:commandButton value="Jump!" actionListener="#{cc.attrs.model.jump}"/>
	</h:panelGrid>
</cc:implementation>
</html>
cc:attribute "model" should point to a managed bean having attributes "labelValue", "inputValue" and method (actionListener) "jump". Using of this custom component is simple
<h:form>
	...
	<custom:labeledInput id="cli1" model="#{someBean}">
	...
</h:form>

<h:form>
	...
	<custom:labeledInput id="cli2" model="#{someBean}">
	...
</h:form>
There is a problem with this JSF solution. If you change the input value (field h:inputText) in the first component and submit the surrounding form, the new value is going to set into the bean "someBean". This bean is also used by the second component. That means, the second component gets the changed value what is not a desired behavior of course. But JSF is powerful enough to solve this problem. There is a possibility to mix JSF components written in Java with declarative code written in XHTML. We can write a class LabeledInput extending UINamingContainer (according to specification) for our composite component. This class encapsulates then all attributes and has the mentioned above actionListener method.
@FacesComponent("mypackage.LabeledInput")
public class LabeledInput extends UINamingContainer
{
	enum PropertyKeys {labelValue, inputValue, jump;}

    public Object getLabelValue() {
		return getStateHelper().eval(PropertyKeys.labelValue);
    }

    public void setLabelValue(Object labelValue) {
		getStateHelper().put(PropertyKeys.labelValue, labelValue);
    }

    public Object getInputValue() {
		return getStateHelper().eval(PropertyKeys.inputValue);
    }

    public void setInputValue(Object inputValue) {
		getStateHelper().put(PropertyKeys.inputValue, inputValue);
    }

	public void jump(ActionEvent e) {
		// do something
	}
}
Component class can be linked now with the XHTML part via "componentType" attribute in cc:interface. Important part to be changed looks then as follows
<cc:interface componentType="mypackage.LabeledInput">
	<cc:attribute name="labelValue"/>
	<cc:attribute name="inputValue"/>
	<cc:actionSource name="jump"/>
</cc:interface>
<cc:implementation>
	<h:panelGrid columns="3">
		<h:outputLabel for="#{cc.clientId}:input" value="#{cc.labelValue}"/>
		<h:inputText id="input" value="#{cc.inputValue}"/>
		<h:commandButton value="Jump!" actionListener="#{cc.jump}"/>
	</h:panelGrid>
</cc:implementation>
Managed bean is not needed to be passed by this way. Now we can speak about an autonomous and reusable composite component and write <custom:labeledInput> on a page as much as we want without any collisions.