10

CellTable内で使用されているCompositeCellの適切な例がないのはなぜですか?のフォローアップとして

JSR-303検証サポートを追加しようとしています。私はここでKomaの設定アドバイスに従いました:gwt-2.4.0でgwt-validationをインストールする方法(注:GWT-ValidationではなくGWT 2.4の組み込み検証を使用しています)。

繰り返しになりますが、再利用するために、ValidatableInputCellAbstractValidatableColumnというクラスのペアを作成しました。私は彼らからインスピレーションを得ました:

'日を見てみましょう...

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 &lt;input&gt; 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;
    }
}

}

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;
}

}

私はAbstractValidatableColumnを次のように使用します...

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;
}

おー!これがJSR-303アノテーション付きのDTOです...

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;

...

}

onBrowserEventにブレークポイントをドロップする各キーストロークで、および/またはセルがフォーカスを失った後に検証トリガーが発生することを期待します。呼び出されることはありません。セルには好きなものを入力できます。修正するためのアプローチに関する手がかりはありますか?

私の初期の考え...a)AbstractValidatableColumn#getFieldUpdaterが呼び出されることはなく、b)ValidatableInputCell#onBrowserEventまたはValidatableInputCell#renderのいずれかのロジックをオーバーホールする必要があります。

最終的には、制約に違反する各セルの横にポップアップが表示されることを望んでいます。もちろん、適切な色が適用されていることを確認してください。

4

2 に答える 2

3

ここで空気を求めてやってくる。 私はついに解決策を見つけました!GWT検証ライブラリ を使用することを選択しました。http://code.google.com/p/gwt-validation/wiki/GWT_Validation_2_0を参照してください(以下のコードは2.1 SNAPSHOTで動作することがわかっています)。

セルの検証を実行するときの秘訣は、validateではなくvalidateValueを呼び出すことです(後者はすべてのエンティティのフィールドの検証をトリガーします)。同様に、すべての入力セル値は文字列であり、検証される前にそれぞれのエンティティフィールドタイプに変換されます。(ネストされたエンティティフィールドでも機能します)。

AbstractValidatableColumn(AVC)とValidatableInputCellの両方の改訂されたimplを次に示します。

/**
 * A {@link Column} implementation that encapsulates a {@link ValidatableInputCell}.
 * Performs JSR-303 validation on a field (or nested field) of the type.
 * @author cphillipson
 *
 * @param <T> the type
 * @param <O> the owning type of the field to be validated; in many cases T may have only primitive or wrapper types, therefore O will be the same type as T
 */
public abstract class AbstractValidatableColumn<T, O> extends Column<T, String> {

/**
 * Preferred constructor.
 * Allows for definition of tabIndex but uses a default for the input cell size.
 * @param tabIndex the <code>tabindex</code> attribute's value for the input cell
 * @param table the grid instance
 */
public AbstractValidatableColumn(int tabIndex, final AbstractHasData<T> table) {
    this(App.INSTANCE.defaultValidatableInputCellSize(), tabIndex, table);
}

/**
 * Overloaded constructor.
 * Allows for definition of tabIndex and allows for an override to the default for the input cell size.
 * @param inputSize the <code>size</code> attribute's value for the input cell
 * @param tabIndex the <code>tabindex</code> attribute's value for the input cell
 * @param table the grid instance
 */
public AbstractValidatableColumn(int inputSize, int tabIndex, final AbstractHasData<T> table) {
    super(new ValidatableInputCell());
    getCell().setInputSize(inputSize);
    getCell().setTabIndex(tabIndex);
    init(table);
}

// meat and potatoes
private void init(final AbstractHasData<T> table) {
    setFieldUpdater(new FieldUpdater<T, String>() {
        @Override
        public void update(int index, T dto, String newValue) {
            final ConversionResult cr = attemptValueConversion(newValue);
            final ValidationData viewData = getCell().getViewData(dto);
            if (cr.wasConvertedSuccessfully()) {
                final Set<ConstraintViolation<O>> violations = validate(cr.getValue());
                if (!violations.isEmpty()) {  // invalid
                    final StringBuffer errorMessage = new StringBuffer();
                    for (final ConstraintViolation<O> constraintViolation : violations) {
                        errorMessage.append(constraintViolation.getMessage());
                    }
                    viewData.setInvalid(true);
                    getCell().setErrorMessage(errorMessage.toString());
                } else {  // valid
                    viewData.setInvalid(false);
                    getCell().setErrorMessage("");
                    doUpdate(index, dto, newValue);
                }
            } else { // conversion exception
                viewData.setInvalid(true);
                getCell().setErrorMessage(UiMessages.INSTANCE.improper_input_format());
            }
        }
    });
}


/**
 * Attempts conversion of a String value into another type
 * Instances are responsible for the conversion logic as it may vary from type to type
 * @param value a String value to be converted into an owning class's property type
 * @return a ConversionResult
 */
protected abstract ConversionResult attemptValueConversion(String value);

@Override
public ValidatableInputCell getCell() {
    return (ValidatableInputCell) super.getCell();
}

/**
 * Template method for updating a field (or nested field) value within a DTO
 * @param index the row index for the instance of the DTO within the grid
 * @param dto the object whose field we wish to update
 * @param value the new value that will be set on a field (or nested field) of the DTO
 */
protected abstract void doUpdate(int index, T dto, String value);

/**
 * Template method for specifying the property name of an owning class
 * @return the field name of the owning class whose value is to be updated
 */
protected abstract String getPropertyName();

/**
 * Template method for specifying the owning class
 * @return the owning class of the field whose value is to be updated
 */
protected abstract Class<O> getPropertyOwner();

/**
 * Validates a value against a set of constraints (i.e., JSR-303 annotations on a field)
 * @param newValue the value to be validated
 * @return the set of constraint violations induced by an inappropriate value
 */
protected Set<ConstraintViolation<O>> validate(Object newValue) {
    final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
    final Set<ConstraintViolation<O>> violations = validator.validateValue(getPropertyOwner(), getPropertyName(), newValue);
    return violations;
}

}

/**
 * <p>A cell that will update its styling and provide feedback upon a validation constraint violation.</p>
 * <p>Implementation based upon GWT Showcase's <a href="http://gwt.google.com/samples/Showcase/Showcase.html#!CwCellValidation">Cell Validation</a> example.</p>
 * @author cphillipson
 *
 */
 public class ValidatableInputCell extends AbstractInputCell<String, ValidatableInputCell.ValidationData> {

interface Template extends SafeHtmlTemplates {
    @Template("<input type=\"text\" value=\"{0}\" size=\"{1}\" style=\"{2}\" tabindex=\"{3}\"></input>")
    SafeHtml input(String value, String width, SafeStyles color, String tabIndex);
}

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 = App.INSTANCE.defaultValidatableInputCellSize();

/**
 * Specifies the width, in characters, of the &lt;input&gt; element contained within this cell
 */
private int inputSize = DEFAULT_INPUT_SIZE;

/**
 * Specifies the tab index for this cell
 */
private int tabIndex = -1;

public ValidatableInputCell() {
    // since onBrowserEvent method is overridden, we must register all events that handled in overridden method impl
    super("change", "keyup", "focus", "blur", "keydown");
    if (template == null) {
        template = GWT.create(Template.class);
    }
}

public void setInputSize(int inputSize) {
    this.inputSize = inputSize;
}

public void setTabIndex(int index) {
    tabIndex = index;
}

public void setErrorMessage(String errorMessage) {
    this.errorMessage = SafeHtmlUtils.fromSafeConstant(errorMessage).asString();
}

@Override
public void onBrowserEvent(Context context, Element parent, String value,
        NativeEvent event, ValueUpdater<String> valueUpdater) {
    super.onBrowserEvent(context, parent, value, event, valueUpdater);

    final InputElement input = (InputElement) getInputElement(parent);
    final Object key = context.getKey();
    final String eventType = event.getType();

    if ("keyup".equals(eventType)) {

        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);
    }
}

@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 ? App.INSTANCE.invalidCellInputTextColor() : App.INSTANCE.pendingCellInputTextColor() : App.INSTANCE.defaultCellInputTextColor();
    final String backgroundColor = pendingValue != null ? invalid ? App.INSTANCE.invalidCellInputTextBackgroundColor() : App.INSTANCE.pendingCellInputTextBackgroundColor() : App.INSTANCE.defaultCellInputTextBackgroundColor();
    final SafeStyles style = SafeStylesUtils.fromTrustedString("color: " + color + "; background-color: " + backgroundColor + ";");
    sb.append(template.input(pendingValue != null ? pendingValue : value, String.valueOf(inputSize), style, String.valueOf(tabIndex)));
}

/*
@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 onEnterKeyDown(Context context, Element parent, String value,
        NativeEvent event, ValueUpdater<String> valueUpdater) {
    // do nothing
}

@Override
protected void finishEditing(Element parent, String value, Object key,
        ValueUpdater<String> valueUpdater) {

    // Update the value updater, which updates the field updater.
    if (valueUpdater != null) {
        valueUpdater.update(value);
    }

    final InputElement input = (InputElement) getInputElement(parent);
    final ValidationData viewData = getViewData(key);

    /*
     * 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 ? App.INSTANCE.invalidCellInputTextColor() : App.INSTANCE.pendingCellInputTextColor() : App.INSTANCE.defaultCellInputTextColor();
    final String backgroundColor = pendingValue != null ? invalid ? App.INSTANCE.invalidCellInputTextBackgroundColor() : App.INSTANCE.pendingCellInputTextBackgroundColor() : App.INSTANCE.defaultCellInputTextBackgroundColor();
    input.getStyle().setColor(color);
    input.getStyle().setBackgroundColor(backgroundColor);

    if (invalid) {
        final DecoratedPopupPanel errorMessagePopup = new DecoratedPopupPanel(true);
        final FlowPanel messageContainer = new FlowPanel();
        messageContainer.setWidth(App.INSTANCE.errorMessagePopupWidth());
        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() +5;
        final int top = parent.getAbsoluteTop() - 5;

        errorMessagePopup.setPopupPositionAndShow(new PopupPanel.PositionCallback() {
            @Override
            public void setPosition(int offsetWidth, int offsetHeight) {
                errorMessagePopup.setPopupPosition(left, top);
            }
        });
    }

}

/**
 * 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;
    }
}

}

AVCのバリアントは次のようになります...

/**
 * A variant of {@link AbstractValidatableColumn} that works with {@link BigDecimal} field types.
 * @author cphillipson
 *
 * @param <T> the type
 * @param <O> the owning type of the field to be validated; in many cases T may have only primitive or wrapper types, therefore O will be the same type as T
 */
public abstract class BigDecimalValidatableColumn<T, O> extends AbstractValidatableColumn<T, O> {

public BigDecimalValidatableColumn(int tabIndex, AbstractHasData table) {
    super(tabIndex, table);
}

public BigDecimalValidatableColumn(int inputSize, int tabIndex, final AbstractHasData<T> table) {
    super(inputSize, tabIndex, table);
}

@Override
protected ConversionResult attemptValueConversion(String value) {
    return doConversion(value);
}

public static ConversionResult doConversion(String value) {
    ConversionResult result = null;
    try {
        final Double dblValue = Double.valueOf(value);
        final BigDecimal convertedValue = BigDecimal.valueOf(dblValue);
        result = ConversionResult.converted(convertedValue);
    } catch (final NumberFormatException nfe) {
        result = ConversionResult.not_converted();
    }
    return result;
}
}

ConversionResultは、列のfieldUpdaterによって参照されます。これがどのように見えるかです...

/**
 * An attempted conversion result.
 * Returns both the converted value (from <code>String</code>) and whether or not the conversion was successful.
 * E.g., if you tried to convert from a <code>String</code> to a <code>Number</code>, in the failure case this would result in a <code>NumberFormatException</code>.
 * On failure, the boolean would be false and the value would be null.
 * On success, the boolean would be true and the value would be of the type needed to continue validation against a set of constraints
 * @author cphillipson
 *
 */
public  class ConversionResult {
private Object value;
private boolean convertedSuccessfully;

private ConversionResult () {}

/**
 * Use this method when a successful conversion is made to return a result
 * @param value the convertedValue
 * @return the result of the conversion containing the converted value and a success flag
 */
public static ConversionResult converted(Object value) {
    final ConversionResult result = new ConversionResult();
    result.setConvertedSuccessfully(true);
    result.setValue(value);
    return result;
}

/**
 * Use this method when an attempt to convert a String value failed
 * @return the result of a failed conversion
 */
public static ConversionResult not_converted() {
    return new ConversionResult();
}

private void setValue(Object value) {
    this.value = value;
}

public Object getValue() {
    return value;
}

private void setConvertedSuccessfully(boolean flag) {
    convertedSuccessfully = flag;
}

public boolean wasConvertedSuccessfully() {
    return convertedSuccessfully;
}
}

最後に、グリッド内の列を指定する方法は次のとおりです

new BigDecimalValidatableColumn<EnergyOfferDTO, OfferPriceMwPairDTO>(nextTabIndex(), getGrid()) {

            @Override
            public String getValue(EnergyOfferDTO energyOffer) {
                return obtainPriceValue(colIndex, energyOffer, false);
            }

            @Override
            public void doUpdate(int index, EnergyOfferDTO energyOffer, String value) {
                if (value != null && !value.isEmpty()) {
                    // 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);
                    final List<OfferPriceMwPairDTO> offerPriceCurve = energyOffer.getCurve();
                    final OfferPriceMwPairDTO offerPriceMwPairDTO = offerPriceCurve.get(colIndex);
                    if (offerPriceMwPairDTO == null) {  // we have a new price value
                        newOfferPriceMwPair.setPrice(price);
                        offerPriceCurve.add(newOfferPriceMwPair);
                    } else {
                        offerPriceMwPairDTO.setPrice(price);
                    }

                }
            }

            @Override
            protected String getPropertyName() {
                return "price";
            }

            @Override
            protected Class<OfferPriceMwPairDTO> getPropertyOwner() {
                return OfferPriceMwPairDTO.class;
            }

        };

上記の例のDTOには、フィールドJSR-303制約に注釈が付けられていることに注意してください(たとえば、@ Digits、@ NotNullを使用)。

上記はある程度の作業を要しましたが、現時点でネット上で最も包括的なソリューションである可能性があります。楽しみ!

于 2012-05-16T16:36:33.097 に答える
1

なぜaHasCellが返されるのかは私にはわかりません。なぜなら、それはgeneratePriceColumn実質的に何でも消費できないからです。ただし、CompositeCellこれらすべてをより大きなセルにまとめようとしているのかもしれません。質問する前に、将来、例をさらに細かく分析すると、問題が明らかになる可能性があります。

'column'作成コードを変更して、実際にColumnを返すようにしました。これは、AbstractValidatableColumnを変更してColumnを拡張することを意味します。途中で、基になるフィールドを変更せずにgetFieldUpdaterをオーバーライドしていることに気付きました。これにより、他の列の内部がそのフィールドを探すときに機能しなくなります。この結果、私の最初の実験では、ValidatableInputCell.onBrowserEventキーアップケースに正しく到達していましたが、ColumnでがnullであったValueUpdaterため、操作するインスタンスがありませんでした。FieldUpdater

その時点で、私が接続しなかった検証ロジックが呼び出されています-GWT 2.4.0の時点で、これはすべてのクラスで「EXPERIMENTAL」としてタグ付けされており、本番コードでは使用されないため、粗いエッジが丸められた2.5.0かそこらまでパスを与えました。ただし、続行する場合(および問題がある場合)は、http://code.google.com/p/google-web-toolkit/source/browse/trunk/samples/validation/のプロジェクトから開始します。 -それを機能させてから、私のものも機能するまで詳細を盗みます。

他のいくつかの観察:

クラスを拡張して機能を追加しないでください。ただし、そのクラスのコンシューマーがサブクラスと同じようにクラスを使用することを期待/許可する場合を除きます。この場合はわかりにくいですが、サブクラスにあるgeneratePriceColumnように見えます。CellTable

  1. それを使用するコードで、セルテーブルの残りの部分の設定方法を変更できます。
  2. 実際にはメソッドのようには機能しませんCellTable-他の列に焦点を当てたメソッドは、列を返す代わりに実際に列を追加します
  3. 常に使用するようにロックインする可能性がありますCellTableが(それがサブクラス化されているため)、そのメソッドは、新しいAbstractCellTableサブクラスのようなサブクラスで完全に機能します。DataTableCellTable

この場合、メソッドをに変更しaddPriceColumn(...)、Columnを使用してリストに追加するか、サブクラス化された列として、またはユーティリティメソッドとして完全に単独で除外します。私の最後のAbstractValidationColumnは、サブクラスになる理由がまったくなく、事実上、Columnの便利なコンストラクターにすぎませんでした。

public abstract class AbstractValidatableColumn<T> extends Column<T, String> {

  public AbstractValidatableColumn(int inputSize, final AbstractCellTable<T> table) {
    super(new ValidatableInputCell());
    ((ValidatableInputCell) getCell()).setInputSize(inputSize);

    setFieldUpdater(new FieldUpdater<T, String>() {
      public void update(int index, T dto, String value) {
        final Set<ConstraintViolation<T>> violations = validate(dto);
        final ValidationData viewData = getCell().getViewData(dto);
        if (!violations.isEmpty()) {  // invalid
          final StringBuffer errorMessage = new StringBuffer();
          for (final ConstraintViolation<T> constraintViolation : violations) {
            errorMessage.append(constraintViolation.getMessage());
          }
          viewData.setInvalid(true);
          getCell().setErrorMessage(errorMessage.toString());
          table.redraw();
        } else {  // valid
          viewData.setInvalid(false);
          getCell().setErrorMessage(null);
          doUpdate(index, dto, value);
        }
      }
    });
  }

  @Override
  public ValidatableInputCell getCell() {
    return (ValidatableInputCell)super.getCell();
  }

  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;
  }
}

FieldUpdaterはここで興味深い部分です。つまり、焦点を当て、他の多くの部分をできるだけ多く再利用できるようにする必要があります。これにより、準備ができたときに任意のセルで独自のValueUpdaterを実行できるようになります。おそらく、必要な頻度ではありませんが、通常は、より簡単に使用できるようになります。別のFieldUpdaterをラップするFieldUpdaterimplを作成します。これは、その場合に変更されるフィールドに固有にすることができます。

ここには別のバグが潜んでいると思います。column/fieldupdaterを単独でテストすると、新しい値がタイプTのBeanに適用されないため、検証が実行されるまでBeanが検証されます。古い有効な値。doUpdate早く呼び出す必要があります。

そして最後に、例を単純化していくことをお勧めします。検証のための「脳死」チェックと、単純で単純なCellTableセットアップにより、列自体に検証機能のみが機能していることがわかります。Column.fieldUpdaterフィールドがnull以外の場合。動作するより単純な構成から構築するため、各段階で問題が発生する可能性があるのは1つだけです。

于 2012-02-27T05:03:22.713 に答える