SWF-844 - Dojo DateTextBox locale-specific formatting is not progressive

SWF-853 - Decorating a form submit button as a ajax submit with a validate-all submission constraint should be easier  
SWF-910 - Make the clientDateValidator JSF component more flexible
This commit is contained in:
Jeremy Grelle
2008-10-14 13:45:47 +00:00
parent f19dddd6f0
commit addca24e6a
8 changed files with 74 additions and 154 deletions

View File

@@ -15,6 +15,12 @@
*/
package org.springframework.faces.ui;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.convert.DateTimeConverter;
import org.springframework.util.Assert;
/**
* Component that uses the Dojo implementation of Spring JavaScript to decorate a child input component with client-side
* date validation behavior.
@@ -26,12 +32,52 @@ public class DojoClientDateValidator extends DojoDecoration {
private static final String DOJO_COMPONENT_TYPE = "dijit.form.DateTextBox";
private static final String[] DOJO_ATTRS_INTERNAL = new String[] { "datePattern" };
private static final String[] DOJO_ATTRS;
private String datePattern = null;
static {
DOJO_ATTRS = new String[DojoDecoration.DOJO_ATTRS.length + DOJO_ATTRS_INTERNAL.length];
System.arraycopy(DojoDecoration.DOJO_ATTRS, 0, DOJO_ATTRS, 0, DojoDecoration.DOJO_ATTRS.length);
System.arraycopy(DOJO_ATTRS_INTERNAL, 0, DOJO_ATTRS, DojoDecoration.DOJO_ATTRS.length,
DOJO_ATTRS_INTERNAL.length);
}
public String getDatePattern() {
Assert.isTrue(getChildren().get(0) instanceof ValueHolder,
"Date validation can only be applied to an ValueHolder");
ValueHolder child = (ValueHolder) getChildren().get(0);
if (child.getConverter() instanceof DateTimeConverter) {
return ((DateTimeConverter) child.getConverter()).getPattern();
}
return datePattern;
}
public void setDatePattern(String datePattern) {
this.datePattern = datePattern;
}
protected String[] getDojoAttributes() {
return DojoDecoration.DOJO_ATTRS;
return DOJO_ATTRS;
}
public String getDojoComponentType() {
return DOJO_COMPONENT_TYPE;
}
public Object saveState(FacesContext context) {
Object[] values = new Object[2];
values[0] = super.saveState(context);
values[1] = this.datePattern;
return values;
}
public void restoreState(FacesContext context, Object state) {
Object values[] = (Object[]) state;
super.restoreState(context, values[0]);
this.datePattern = (String) values[1];
}
}

View File

@@ -16,18 +16,14 @@
package org.springframework.faces.ui;
import java.io.IOException;
import java.util.Date;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import org.springframework.faces.ui.resource.ResourceHelper;
import org.springframework.faces.webflow.JsfUtils;
import org.springframework.util.StringUtils;
/**
* Generic renderer for components that use the Dojo implementation of Spring JavaScript to decorate a child component
@@ -78,13 +74,8 @@ public class DojoDecorationRenderer extends BaseSpringJavascriptDecorationRender
script.append(" widgetType : '" + ((DojoDecoration) component).getDojoComponentType() + "', ");
script.append(" widgetAttrs : { ");
String nodeAttrs = getNodeAttributesAsString(context, advisedChild);
String dojoAttrs = getDojoAttributesAsString(context, component);
script.append(nodeAttrs);
if (StringUtils.hasText(dojoAttrs)) {
script.append(", ");
}
script.append(dojoAttrs);
script.append(" }}));");
@@ -94,50 +85,6 @@ public class DojoDecorationRenderer extends BaseSpringJavascriptDecorationRender
ResourceHelper.endScriptBlock(context);
}
protected String getNodeAttributesAsString(FacesContext context, UIComponent component) {
StringBuffer attrs = new StringBuffer();
attrs.append("name : '" + component.getClientId(context) + "'");
ValueHolder valueHolder = (ValueHolder) component;
if (valueHolder.getValue() != null) {
attrs.append(", value : ");
String strValue = "'" + getValueAsString(context, component) + "'";
if (valueHolder.getValue() instanceof Date) {
strValue = "dojo.date.locale.parse(" + strValue + ", {selector : 'date', datePattern : 'yyyy-MM-dd'})";
}
attrs.append(strValue);
}
return attrs.toString();
}
protected String getValueAsString(FacesContext context, UIComponent component) {
ValueHolder valueHolder = (ValueHolder) component;
if (valueHolder.getValue() instanceof String) {
return valueHolder.getValue().toString();
}
Converter converter;
if (valueHolder.getConverter() != null) {
converter = valueHolder.getConverter();
} else {
converter = context.getApplication().createConverter(valueHolder.getValue().getClass());
}
if (converter == null) {
throw new FacesException("A converter could not be found to convert the value of " + component
+ " to a String.");
}
return converter.getAsString(context, component, valueHolder.getValue());
}
protected String getDojoAttributesAsString(FacesContext context, UIComponent component) {
DojoDecoration advisor = (DojoDecoration) component;

View File

@@ -1,89 +0,0 @@
package org.springframework.faces.ui;
import java.util.Calendar;
import java.util.Locale;
import javax.faces.FacesException;
import javax.faces.component.UIInput;
import javax.faces.convert.DateTimeConverter;
import junit.framework.TestCase;
import org.springframework.faces.webflow.JSFMockHelper;
public class DojoDecorationRendererTests extends TestCase {
JSFMockHelper jsf = new JSFMockHelper();
public void setUp() throws Exception {
jsf.setUp();
}
public void tearDown() throws Exception {
jsf.tearDown();
}
public void testGetValueAsString() {
UIInput childComponent = new UIInput();
childComponent.setValue("foo");
DojoDecorationRenderer renderer = new DojoDecorationRenderer();
String convertedValue = renderer.getValueAsString(jsf.facesContext(), childComponent);
assertEquals("foo", convertedValue);
}
public void testGetValueAsString_LocalConverter() {
UIInput childComponent = new UIInput();
childComponent.setValue(new TestValue());
childComponent.setConverter(new TestConverter());
DojoDecorationRenderer renderer = new DojoDecorationRenderer();
String convertedValue = renderer.getValueAsString(jsf.facesContext(), childComponent);
assertEquals("foo", convertedValue);
}
public void testGetValueAsString_NoConverter() {
UIInput childComponent = new UIInput();
childComponent.setValue(new TestValue());
DojoDecorationRenderer renderer = new DojoDecorationRenderer();
try {
renderer.getValueAsString(jsf.facesContext(), childComponent);
fail("getValueAsString should throw exception if no converter is found");
} catch (FacesException ex) {
// expected
}
}
public void testGetValueAsString_GlobalConverter() throws Exception {
UIInput childComponent = new UIInput();
childComponent.setValue(new TestValue());
jsf.facesContext().getApplication().addConverter(TestValue.class, TestConverter.class.getName());
DojoDecorationRenderer renderer = new DojoDecorationRenderer();
String convertedValue = renderer.getValueAsString(jsf.facesContext(), childComponent);
assertEquals("foo", convertedValue);
}
public void testGetNodeAttributesAsString() {
String expectedAttributes = "name : 'foo', value : 'foo'";
UIInput childComponent = new UIInput();
childComponent.setId("foo");
childComponent.setValue("foo");
DojoDecorationRenderer renderer = new DojoDecorationRenderer();
String nodeAttributes = renderer.getNodeAttributesAsString(jsf.facesContext(), childComponent);
assertEquals(expectedAttributes, nodeAttributes);
}
public void testGetNodeAttributesAsString_DateValue() {
String expectedAttributes = "name : 'foo', value : dojo.date.locale.parse('Nov 21, 1977', "
+ "{selector : 'date', datePattern : 'yyyy-MM-dd'})";
UIInput childComponent = new UIInput();
DateTimeConverter converter = new DateTimeConverter();
converter.setLocale(Locale.US);
childComponent.setConverter(converter);
childComponent.setId("foo");
Calendar cal = Calendar.getInstance(Locale.US);
cal.set(1977, Calendar.NOVEMBER, 21, 12, 0);
childComponent.setValue(cal.getTime());
DojoDecorationRenderer renderer = new DojoDecorationRenderer();
String nodeAttributes = renderer.getNodeAttributesAsString(jsf.facesContext(), childComponent);
assertEquals(expectedAttributes, nodeAttributes);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -41,6 +41,17 @@ dojo.declare("Spring.ElementDecoration", [Spring.AbstractElementDecoration, Spri
console.error("Could not apply " + this.widgetType + " decoration. Element with id '" + this.elementId + "' not found in the DOM.");
}
else {
var datePattern = this.widgetAttrs['datePattern'];
if (datePattern && this.widgetType == 'dijit.form.DateTextBox') {
if (!this.widgetAttrs['value']) {
this.widgetAttrs['value'] = dojo.date.locale.parse(element.value, {selector : "date", datePattern : datePattern});
}
if (!this.widgetAttrs['serialize']) {
this.widgetAttrs['serialize'] = function(d, options){
return dojo.date.locale.format(d, {selector : "date", datePattern : datePattern});
}
}
}
for (var copyField in this.copyFields) {
copyField = this.copyFields[copyField];
if (!this.widgetAttrs[copyField] && element[copyField] &&
@@ -97,7 +108,8 @@ dojo.declare("Spring.ValidateAllDecoration", [Spring.AbstractValidateAllDecorati
},
handleEvent : function(event, context){
if (!Spring.validateAll()) {
event.springValidateAll = Spring.validateAll();
if (!event.springValidateAll) {
dojo.stopEvent(event);
} else if(dojo.isFunction(context.originalHandler)) {
var result = context.originalHandler(event);
@@ -130,7 +142,9 @@ dojo.declare("Spring.AjaxEventDecoration", [Spring.AbstractAjaxEventDecoration,
if(this.formId == ""){
Spring.remoting.getLinkedResource(this.sourceId, this.params, this.popup);
} else {
Spring.remoting.submitForm(this.sourceId, this.formId, this.params);
if (event.springValidateAll){
Spring.remoting.submitForm(this.sourceId, this.formId, this.params);
}
}
dojo.stopEvent(event);
}

View File

@@ -50,9 +50,9 @@
<h:outputLabel for="checkinDate">Check In Date:</h:outputLabel>
</div>
<div class="input">
<sf:clientDateValidator required="true" >
<sf:clientDateValidator required="true">
<h:inputText id="checkinDate" value="#{booking.checkinDate}" required="true">
<f:convertDateTime pattern="yyyy-MM-dd" timeZone="EST"/>
<f:convertDateTime pattern="MM-dd-yyyy" timeZone="EST"/>
</h:inputText>
</sf:clientDateValidator>
</div>
@@ -64,7 +64,7 @@
<div class="input">
<sf:clientDateValidator required="true">
<h:inputText id="checkoutDate" value="#{booking.checkoutDate}" required="true">
<f:convertDateTime pattern="yyyy-MM-dd" timeZone="EST"/>
<f:convertDateTime pattern="MM-dd-yyyy" timeZone="EST"/>
</h:inputText>
</sf:clientDateValidator>
</div>

View File

@@ -10,7 +10,9 @@ public class ApplicationConversionService extends DefaultConversionService {
@Override
protected void addDefaultConverters() {
super.addDefaultConverters();
addConverter("shortDate", new StringToDate());
StringToDate dateConverter = new StringToDate();
dateConverter.setPattern("MM-dd-yyyy");
addConverter("shortDate", dateConverter);
}
}

View File

@@ -40,7 +40,7 @@
Spring.addDecoration(new Spring.ElementDecoration({
elementId : "checkinDate",
widgetType : "dijit.form.DateTextBox",
widgetAttrs : { value : dojo.date.locale.parse(dojo.byId("checkinDate").value, {selector : "date", datePattern : "yyyy-MM-dd"}), required : true }}));
widgetAttrs : { datePattern : "MM-dd-yyyy", required : true }}));
</script>
</div>
</div>
@@ -54,7 +54,7 @@
Spring.addDecoration(new Spring.ElementDecoration({
elementId : "checkoutDate",
widgetType : "dijit.form.DateTextBox",
widgetAttrs : { value : dojo.date.locale.parse(dojo.byId("checkoutDate").value, {selector : "date", datePattern : "yyyy-MM-dd"}), required : true }}));
widgetAttrs : { datePattern : "MM-dd-yyyy", required : true }}));
</script>
</div>
</div>
@@ -170,10 +170,10 @@
</div>
</div>
<div class="buttonGroup">
<input type="submit" id="proceed" name="_eventId_proceed" value="Proceed"
onclick="Spring.remoting.submitForm('proceed', 'booking', {fragments:'messages,bookingForm'}); return false;"/>&#160;
<input type="submit" id="proceed" name="_eventId_proceed" value="Proceed" />&#160;
<script type="text/javascript">
Spring.addDecoration(new Spring.ValidateAllDecoration({elementId:'proceed', event:'onclick'}));
Spring.addDecoration(new Spring.AjaxEventDecoration({elementId:'proceed',event:'onclick',formId:'booking',params:{fragments:'messages,bookingForm'}}));
</script>
<input type="submit" name="_eventId_cancel" value="Cancel"/>&#160;
</div>