Jako kontynuacja doPoszukując przykładu sprawdzania GWT ... gdzie jesteś?
Próbuję dodać obsługę sprawdzania poprawności JSR-303. Śledziłem konfigurację Komy tutaj: How to install gwt-validation with gwt-2.4.0 (Uwaga: Używam wbudowanej weryfikacji GWT 2.4, a nie GWT-Validation).
Ponownie, aby uzyskać pewne ponowne wykorzystanie I spreparowane parę klas, ValidatableInputCell i AbstractValidatableColumn. Mam dla nich inspirację z:
- http://code.google.com/p/google-web-toolkit/source/browse/trunk/samples/validation/src/main/java/com/google/gwt/sample/validation/client/ValidationView.java?r=10642
- http://gwt.google.com/samples/Showcase/Showcase.html#!CwCellValidation
Rzućmy okiem na „em ...
public class ValidatableInputCell extends AbstractInputCell<String, ValidatableInputCell.ValidationData> {
interface Template extends SafeHtmlTemplates {
@Template("<input type=\"text\" value=\"{0}\" size=\"{1}\" style=\"{2}\" tabindex=\"-1\"></input>")
SafeHtml input(String value, String width, SafeStyles color);
}
private static Template template;
/**
* The error message to be displayed as a pop-up near the field
*/
private String errorMessage;
private static final int DEFAULT_INPUT_SIZE = 15;
/**
* Specifies the width, in characters, of the <input> element contained within this cell
*/
private int inputSize = DEFAULT_INPUT_SIZE;
public ValidatableInputCell() {
super("change", "keyup");
if (template == null) {
template = GWT.create(Template.class);
}
}
public void setInputSize(int inputSize) {
this.inputSize = inputSize;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = SafeHtmlUtils.htmlEscape(errorMessage);
}
@Override
public void onBrowserEvent(Context context, Element parent, String value,
NativeEvent event, ValueUpdater<String> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
// Ignore events that don't target the input.
final InputElement input = (InputElement) getInputElement(parent);
final Element target = event.getEventTarget().cast();
if (!input.isOrHasChild(target)) {
return;
}
final Object key = context.getKey();
final String eventType = event.getType();
if ("change".equals(eventType)) {
finishEditing(parent, value, key, valueUpdater);
} else if ("keyup".equals(eventType)) {
// Mark cell as containing a pending change
input.getStyle().setColor("blue");
ValidationData viewData = getViewData(key);
// Save the new value in the view data.
if (viewData == null) {
viewData = new ValidationData();
setViewData(key, viewData);
}
final String newValue = input.getValue();
viewData.setValue(newValue);
finishEditing(parent, newValue, key, valueUpdater);
// Update the value updater, which updates the field updater.
if (valueUpdater != null) {
valueUpdater.update(newValue);
}
}
}
@Override
public void render(Context context, String value, SafeHtmlBuilder sb) {
// Get the view data.
final Object key = context.getKey();
ValidationData viewData = getViewData(key);
if (viewData != null && viewData.getValue().equals(value)) {
// Clear the view data if the value is the same as the current value.
clearViewData(key);
viewData = null;
}
/*
* If viewData is null, just paint the contents black. If it is non-null,
* show the pending value and paint the contents red if they are known to
* be invalid.
*/
final String pendingValue = viewData == null ? null : viewData.getValue();
final boolean invalid = viewData == null ? false : viewData.isInvalid();
final String color = pendingValue != null ? invalid ? "red" : "blue" : "black";
final SafeStyles safeColor = SafeStylesUtils.fromTrustedString("color: " + color + ";");
sb.append(template.input(pendingValue != null ? pendingValue : value, String.valueOf(inputSize), safeColor));
}
@Override
protected void onEnterKeyDown(Context context, Element parent, String value,
NativeEvent event, ValueUpdater<String> valueUpdater) {
final Element target = event.getEventTarget().cast();
if (getInputElement(parent).isOrHasChild(target)) {
finishEditing(parent, value, context.getKey(), valueUpdater);
} else {
super.onEnterKeyDown(context, parent, value, event, valueUpdater);
}
}
@Override
protected void finishEditing(Element parent, String value, Object key,
ValueUpdater<String> valueUpdater) {
final ValidationData viewData = getViewData(key);
final String pendingValue = viewData == null ? null : viewData.getValue();
final boolean invalid = viewData == null ? false : viewData.isInvalid();
if (invalid) {
final DecoratedPopupPanel errorMessagePopup = new DecoratedPopupPanel(true);
final VerticalPanel messageContainer = new VerticalPanel();
messageContainer.setWidth("200px");
final Label messageTxt = new Label(errorMessage, true);
messageTxt.setStyleName(UiResources.INSTANCE.style().error());
messageContainer.add(messageTxt);
errorMessagePopup.setWidget(messageContainer);
// Reposition the popup relative to input field
final int left = parent.getAbsoluteRight() + 25;
final int top = parent.getAbsoluteTop();
errorMessagePopup.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
@Override
public void setPosition(int offsetWidth, int offsetHeight) {
errorMessagePopup.setPopupPosition(left, top);
}
});
}
// XXX let user continue or force focus until value is valid? for now the former is implemented
super.finishEditing(parent, pendingValue, key, valueUpdater);
}
/**
* The ViewData used by {@link ValidatableInputCell}.
*/
static class ValidationData {
private boolean invalid;
private String value;
public String getValue() {
return value;
}
public boolean isInvalid() {
return invalid;
}
public void setInvalid(boolean invalid) {
this.invalid = invalid;
}
public void setValue(String value) {
this.value = value;
}
}
}
i
public abstract class AbstractValidatableColumn<T> implements HasCell<T, String> {
private ValidatableInputCell cell = new ValidatableInputCell();
private CellTable<T> table;
public AbstractValidatableColumn(int inputSize, CellTable<T> table) {
cell.setInputSize(inputSize);
this.table = table;
}
@Override
public Cell<String> getCell() {
return cell;
}
@Override
public FieldUpdater<T, String> getFieldUpdater() {
return new FieldUpdater<T, String>() {
@Override
public void update(int index, T dto, String value) {
final Set<ConstraintViolation<T>> violations = validate(dto);
final ValidationData viewData = cell.getViewData(dto);
if (!violations.isEmpty()) { // invalid
final StringBuffer errorMessage = new StringBuffer();
for (final ConstraintViolation<T> constraintViolation : violations) {
errorMessage.append(constraintViolation.getMessage());
}
viewData.setInvalid(true);
cell.setErrorMessage(errorMessage.toString());
table.redraw();
} else { // valid
viewData.setInvalid(false);
cell.setErrorMessage(null);
doUpdate(index, dto, value);
}
}
};
}
protected abstract void doUpdate(int index, T dto, String value);
protected Set<ConstraintViolation<T>> validate(T dto) {
final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
final Set<ConstraintViolation<T>> violations = validator.validate(dto);
return violations;
}
}
I nas E AbstractValidatableColumn jak tak ...
protected HasCell<ReserveOfferDTO, String> generatePriceColumn(DisplayMode currentDisplayMode) {
HasCell<ReserveOfferDTO, String> priceColumn;
if (isInEditMode(currentDisplayMode)) {
priceColumn = new AbstractValidatableColumn<ReserveOfferDTO>(5, this) {
@Override
public String getValue(ReserveOfferDTO reserveOffer) {
return obtainPriceValue(reserveOffer);
}
@Override
protected void doUpdate(int index, ReserveOfferDTO reserveOffer, String value) {
// number format exceptions should be caught and handled by event bus's handle method
final double valueAsDouble = NumberFormat.getDecimalFormat().parse(value);
final BigDecimal price = BigDecimal.valueOf(valueAsDouble);
reserveOffer.setPrice(price);
}
};
} else {
priceColumn = new Column<ReserveOfferDTO, String>(new TextCell()) {
@Override
public String getValue(ReserveOfferDTO reserveOffer) {
return obtainPriceValue(reserveOffer);
}
};
}
return priceColumn;
}
Oh! A oto Dto z JSR-303 adnotacji ...
public class ReserveOfferDTO extends DateComparable implements Serializable {
private static final long serialVersionUID = 1L;
@NotNull @Digits(integer=6, fraction=2)
private BigDecimal price;
@NotNull @Digits(integer=6, fraction=2)
private BigDecimal fixedMW;
private String dispatchStatus;
private String resourceName;
private String dateTime;
private String marketType;
private String productType;
...
}
Usunięcie przerwania w onBrowserEvent spodziewałbym mieć spust walidacji na każdym naciśnięciu klawisza i/lub po komórka traci ostrość. Nigdy nie zostanie wywołany. Mogę wprowadzić cokolwiek mi się podoba w celi. Jakieś wskazówki dotyczące podejścia do naprawy?
Moje wczesne przemyślenia ... a) AbstractValidatableColumn # getFieldUpdater nigdy nie jest wywoływana i b) logika w ValidatableInputCell # onBrowserEvent lub ValidatableInputCell # render wymaga przebudowy.
Ostatecznie chciałbym zobaczyć wyskakujące okienko obok każdej komórki, które narusza ograniczenie, i oczywiście zobaczyć, że zastosowano odpowiednią kolorystykę.
Przeglądając teraz - możesz chcieć usunąć 'UiResources.INSTANCE.style(). Error()', ponieważ nie skompiluje się bez twojej własnej klasy UiResources. Nadklasa 'DateComparable' prawdopodobnie też powinna pójść, a' generatePriceColumn' mogłaby prawdopodobnie użyć dodatkowego uproszczenia dla klas 'isInEditMode' oraz' DisplayMode'. –
UiResources to mój własny ClientBundle. DateComparable już dawno minęło. Wszystkie daty są ciągiem ISO. Konwertujemy po stronie serwera, abyśmy mogli przekonwertować odpowiednią strefę czasową. –
Czy to tylko ja, czy też walidacja w GWT jest głupio złożonym problemem, kiedy powinno być naprawdę proste? Jesteśmy teraz na etapie, gdzie Google pozwalający społeczność open-source, aby przejąć kontrolę nad GWT i nadal nie da się łatwo sprawdzić poprawność danych wejściowych na polu ... albo ja czegoś brakuje? – slugmandrew