我正在使用 GWT 2.5.0。
我已经为客户端验证反馈实现了一个自定义抽象基础小部件。我的问题与动态内联样式<select/>
和<input type="text"/>
小部件有关,尤其是在 Internet Explorer 中。
我无法让渲染在 Internet Explorer 8/9 上正确且一致地完成其工作。对于其他浏览器,如 Safari、Chrome、Opera、Firefox,我的 impl 功能完全符合我的要求。
基本上,对于待处理的有效输入,我希望上述小部件的背景为黄色,前景色(文本)为蓝色。
我正在使用此处提到的提示和技巧:https ://developers.google.com/web-toolkit/doc/latest/DevGuideIE9 。
我认为基本问题是我与不支持“事件冒泡”的 IE 发生冲突。请参阅GWT 中的“更改”浏览器事件。但我不知道如何解决它。
更新
看来我的实施可能会被扣为人质,直到
http://code.google.com/p/google-web-toolkit/issues/detail?id=7139
是固定的。
这是我的基本抽象类。onBrowserEvent
请注意和render
方法中用于 Internet Explorer 的用户代理“hack” 。(如果有其他方法,请告诉我)。
public abstract class AbstractValidatableInputCell<E extends Element> extends AbstractInputCell<String, ValidationData> {
/**
* The error message text, pop-up, callback, and position offsets -- error message will be displayed near a field that violated constraint.
*/
private String errorMessage;
private DecoratedPopupPanel errorMessagePopup;
private PopupPanel.PositionCallback popupPositionCallback;
private int popupPositionLeft;
private int popupPositionTop;
/**
* Constructs a ValidatableInputCell that renders its text without HTML
* markup.
*/
public AbstractValidatableInputCell(String... consumedEvents) {
super(consumedEvents);
}
public void setErrorMessage(final String errorMessage) {
this.errorMessage = SafeHtmlUtils.fromSafeConstant(errorMessage).asString();
}
@Override
public void onBrowserEvent(final Context context, final Element parent, final String value,
final NativeEvent event, final ValueUpdater<String> valueUpdater) {
super.onBrowserEvent(context, parent, value, event, valueUpdater);
// Ignore events that don't target the input.
final E input = getInputElement(parent);
final Element target = event.getEventTarget().cast();
if (!input.isOrHasChild(target)) {
return;
}
final String eventType = event.getType();
final Object key = context.getKey();
ValidationData vd = getViewData(key);
final boolean invalid = vd == null ? false : vd.isInvalid();
if (BrowserEvents.CHANGE.equals(eventType)) {
finishEditing(parent, value, key, valueUpdater);
} else if (Window.Navigator.getUserAgent().contains("MSIE")
&& (event.getKeyCode() == KeyCodes.KEY_TAB)) {
finishEditing(parent, value, key, valueUpdater);
getInputElement(parent).focus();
} else if (BrowserEvents.KEYUP.equals(eventType)) {
// Record keys as they are typed.
if (vd == null) {
vd = new ValidationData(value);
setViewData(key, vd);
}
vd.setCurrentValue(getValue(input));
} else if (BrowserEvents.MOUSEOVER.equals(eventType)) {
if (invalid) {
showErrorMessagePopup(parent);
}
} else if (BrowserEvents.MOUSEOUT.equals(eventType)) {
if (invalid) {
hideErrorMessagePopup();
}
}
}
protected void onFinishEditing(final String value, final E input, final Object 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 ValidationData viewData = getViewData(key);
final String pendingValue = viewData == null ? null : viewData.getCurrentValue();
final boolean invalid = viewData == null ? false : viewData.isInvalid();
String color;
String backgroundColor;
if (invalid) {
color = App.INSTANCE.invalidCellInputTextColor();
backgroundColor = App.INSTANCE.invalidCellInputTextBackgroundColor();
} else if (pendingValue != null && !pendingValue.equals(value)) {
color = App.INSTANCE.pendingCellInputTextColor();
backgroundColor = App.INSTANCE.pendingCellInputTextBackgroundColor();
} else {
color = App.INSTANCE.defaultCellInputTextColor();
backgroundColor = App.INSTANCE.defaultCellInputTextBackgroundColor();
}
// Mark cell as containing a pending change
input.getStyle().setColor(color);
input.getStyle().setBackgroundColor(backgroundColor);
}
private void showErrorMessagePopup(final Element parent) {
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);
final Node child = parent.getChild(0);
final Element childElement = (Element) child;
// Reposition the popup relative to input field
popupPositionLeft = childElement.getAbsoluteLeft() - 15;
popupPositionTop = childElement.getAbsoluteBottom() + 5;
popupPositionCallback = new PopupPanel.PositionCallback() {
@Override
public void setPosition(final int offsetWidth, final int offsetHeight) {
errorMessagePopup.setPopupPosition(popupPositionLeft, popupPositionTop);
}
};
errorMessagePopup.setPopupPositionAndShow(popupPositionCallback);
}
private void hideErrorMessagePopup() {
errorMessagePopup.hide();
}
@Override
public void render(final Context context, final String value, final SafeHtmlBuilder sb) {
// Get the view data.
final Object key = context.getKey();
final ValidationData viewData = getViewData(key);
final String pendingValue = viewData == null ? null : viewData.getCurrentValue();
final boolean invalid = viewData == null ? false : viewData.isInvalid();
String inputValue = value;
if (pendingValue != null) {
inputValue = pendingValue;
}
String color;
String backgroundColor;
if (invalid) {
color = App.INSTANCE.invalidCellInputTextColor();
backgroundColor = App.INSTANCE.invalidCellInputTextBackgroundColor();
} else if (pendingValue != null) {
color = App.INSTANCE.pendingCellInputTextColor();
backgroundColor = App.INSTANCE.pendingCellInputTextBackgroundColor();
} else {
color = App.INSTANCE.defaultCellInputTextColor();
backgroundColor = App.INSTANCE.defaultCellInputTextBackgroundColor();
}
// shame on you IE!
if (Window.Navigator.getUserAgent().contains("MSIE") && viewData != null && !viewData.isPended() && !invalid) {
color = App.INSTANCE.defaultCellInputTextColor();
backgroundColor = App.INSTANCE.defaultCellInputTextBackgroundColor();
}
finishRender(inputValue, color, backgroundColor, sb);
}
protected abstract void finishRender(final String inputValue, final String color, final String backgroundColor, final SafeHtmlBuilder sb);
@Override
protected void finishEditing(final Element parent, final String value, final Object key,
final ValueUpdater<String> valueUpdater) {
final String newValue = getNewValue(parent);
// Get the view data.
ValidationData vd = getViewData(key);
if (vd == null) {
vd = new ValidationData(value);
setViewData(key, vd);
}
vd.setCurrentValue(newValue);
// Fire the value updater if the value has changed.
if (valueUpdater != null && !vd.getCurrentValue().equals(vd.getLastValue())) {
vd.setLastValue(newValue);
valueUpdater.update(newValue);
}
onFinishEditing(value, getInputElement(parent), key);
// Blur the element.
super.finishEditing(parent, newValue, key, valueUpdater);
}
@Override
protected E getInputElement(final Element parent) {
return super.getInputElement(parent).<E> cast();
}
protected abstract String getValue(E element);
protected abstract String getNewValue(Element parent);
}
这是一个示例子类(对于input
):
public class ValidatableInputCell extends AbstractValidatableInputCell<InputElement> {
interface Template extends SafeHtmlTemplates {
@Template("<input type=\"text\" value=\"{0}\" size=\"{1}\" style=\"color: {2}; background-color: {3}; display: block; text-align: right\"></input>")
SafeHtml input(String value, String width, String color, String backgroundColor);
}
private static Template template;
private static final int DEFAULT_INPUT_SIZE = App.INSTANCE.defaultValidatableInputCellSize();
/**
* Specifies the width, in characters, of the <input> element contained within this cell
*/
private int inputSize = DEFAULT_INPUT_SIZE;
/**
* Constructs a ValidatableInputCell that renders its text without HTML
* markup.
*/
public ValidatableInputCell() {
// Since onBrowserEvent method is overridden, we must register all
// events that handled in overridden method impl
// Events below are added in
// AbstractInputCell#getConsumedEventsImpl(Set<String> userEvents)
super(BrowserEvents.CHANGE, BrowserEvents.KEYUP, BrowserEvents.MOUSEOVER, BrowserEvents.MOUSEOUT);
if (template == null) {
template = GWT.create(Template.class);
}
}
public void setInputSize(final int inputSize) {
this.inputSize = inputSize;
}
@Override
public void finishRender(final String inputValue, final String color, final String backgroundColor, final SafeHtmlBuilder sb) {
sb.append(template.input(inputValue, String.valueOf(inputSize), color, backgroundColor));
}
@Override
protected String getValue(InputElement element) {
return element.getValue();
}
@Override
protected String getNewValue(Element parent) {
return getValue(getInputElement(parent));
}
}
这是另一个示例子类(for select
):
public class ReferenceDataBackedSelectionCell extends AbstractValidatableInputCell<SelectElement> {
interface SelectTemplate extends SafeHtmlTemplates {
@Template("<select style=\"color: {0}; background-color: {1};\">")
SafeHtml select(String color, String backgroundColor);
}
interface OptionTemplate extends SafeHtmlTemplates {
@Template("<option style=\"color: {2}; background-color: {3}; text-align: right\" value=\"{0}\">{1}</option>")
SafeHtml deselected(String optionSubmitValue, String optionDisplayValue, String color, String backgroundColor);
@Template("<option style=\"color: {2}; background-color: {3}; text-align: right\" value=\"{0}\" selected=\"selected\">{1}</option>")
SafeHtml selected(String optionSubmitValue, String optionDisplayValue, String color, String backgroundColor);
}
private static SelectTemplate selectTemplate;
private static OptionTemplate optionTemplate;
private final Map<String, String> options;
/**
* Construct a new {@link ReferenceDataBackedSelectionCell} from
* {@link ReferenceData}.
*
* @param refData data used to create the options in the cell
*/
public ReferenceDataBackedSelectionCell(final ReferenceData refData) {
super(BrowserEvents.CHANGE);
if (optionTemplate == null) {
optionTemplate = GWT.create(OptionTemplate.class);
}
if (selectTemplate == null) {
selectTemplate = GWT.create(SelectTemplate.class);
}
if (refData == null) {
throw new IllegalArgumentException("Reference data for selection cell may not be null");
}
options = new LinkedHashMap<String, String>(refData.allData());
}
@Override
public void finishRender(final String inputValue, final String color, final String backgroundColor, final SafeHtmlBuilder sb) {
final String selectedIndex = inputValue == null ? "" : inputValue;
sb.append(selectTemplate.select(color, backgroundColor));
String optionSubmitValue, optionDisplayValue = null;
for (final Map.Entry<String, String> option : options.entrySet()) {
optionSubmitValue = option.getValue();
optionDisplayValue = option.getKey();
if (optionSubmitValue.equals(selectedIndex)) {
sb.append(optionTemplate.selected(optionSubmitValue, optionDisplayValue, color, backgroundColor));
} else {
sb.append(optionTemplate.deselected(optionSubmitValue, optionDisplayValue, color, backgroundColor));
}
}
sb.appendHtmlConstant("</select>");
}
@Override
protected String getValue(SelectElement element) {
return element.getValue();
}
@Override
protected String getNewValue(Element parent) {
final SelectElement select = parent.getFirstChild().cast();
final Collection<String> optionCollection = options.values();
final String[] array = optionCollection.toArray(new String[optionCollection.size()]);
final String newValue = array[select.getSelectedIndex()];
return newValue;
}
}