In Wicket 6 the FloatConverter class had a setNumberFormat method.
In Wicket 7 it is no longer available.
I use it to modify the default limit of 3 decimals for a text field.
Is there a different way to do this?
Use AbstractDecimalConverter#newNumberFormat(Locale) for this. You can override it and configure the specifics.
public class MyFloatConverter extends FloatConverter {
#Override protected NumberFormat newNumberFormat(final Locale locale)
{
NumberFormat nf = NumberFormat.getInstance(locale);
nf.setXyz(); // <<- customize the NumberFormat here
return nf;
}
}
The you can setup this converter to be the default one in YourApplication.java:
#Override protected ConverterLocator newConverterLocator()
{
ConverterLocator cl = super.newConverterLocator();
cl.set(Float.class, new MyFloatConverter());
return cl;
}
}
or in a specific Component by overriding its #getConverter(Class) method;
I used your MyFloatConverter like this for my TextField:
#Override
public final IConverter getConverter(Class type) {
FloatConverter fc = new MyFloatConverter();
return fc;
}
Related
Is there any way to make a listener fire once and remove itself?
for(Spec spec : specs){
spec.myProperty().addListener((obs,ov,nv) -> {
if (nv.longValue() > 0){
//do whatever
spec.myProperty().removeListener(this);
}
});
}
That code won't work. I can only think of complicated solutions to this seemingly simple problem.
How about using an anonymous inner class instead of a lambda expression?
IntegerProperty property = new SimpleIntegerProperty();
InvalidationListener listener = new InvalidationListener() {
#Override
public void invalidated(Observable observable) {
//TODO do something
property.removeListener(this);
}
};
property.addListener(listener);
The answer was partly in the comments so I'll add my [brian] solution here.
public void someMethod(){
for(Spec spec : specs){
spec.myProperty().addListener(listener);
}
}
ChangeListener<Number> listener = new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> obs, Number ov, Number nv) {
Spec spec = (Spec)((SimpleLongProperty)obs).getBean();
spec.myProperty().removeListener(this);
}
};
Note, when I create myProperty in the Spec class I use the full constructor to specify the bean. new SimpleLongProperty(this, "myProperty", 0l);
Even doing this you still can't use a lambda to remove this.
I want to create a Cell factory that returns a TableCell that behaves exactly like TextFieldTableCell, with the following difference: When it loses focus, it commits the changes.
My code is very simple:
public final class TextFieldCellFactory<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {
#Override
public TableCell<S, T> call(TableColumn<S, T> p) {
class EditingCell extends TextFieldTableCell {
public EditingCell() {
super();
setConverter(new DefaultStringConverter());
focusedProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
System.out.println("changed!");
System.out.println("getText() = " + getText());
System.out.println("textProperty() = " + textProperty().get());
System.out.println("getItem = " + getItem());
}
});
}
#Override
public void startEdit() {
super.startEdit();
}
#Override
public void cancelEdit() {
super.cancelEdit();
}
}
return new EditingCell();
}
}
As you see I add a change listener in the focusedProperty. The problem is that the change method is not called (nothing is printed).
How can I get the desired behaviour? Thank you.
Basically, you have to register the listener with the textField's (not the cell's) focusedProperty. As the textfield is a private field of super, it's not directly accessible - you have to look it up once after it was added to the cell. That's when an edit was started for the first time:
private TextField myTextField;
#Override
public void startEdit() {
super.startEdit();
if (isEditing() && myTextField == null) {
// most simple case, assuming that there is no graphic other than the field
// TBD: implement the general case: walk the tree and find the field
myTextField = (TextField) getGraphic();
myTextField.focusedProperty().addListener((e, old, nvalue) -> {
if (!nvalue) {
T edited = getConverter().fromString(myTextField.getText());
commitEdit(edited);
}
});
}
}
Some notes:
this is a workaround around an open issue (vote for it!)
since jdk8, it's not entirely functional: won't commit if you click somewhere else inside the table
a recent answer uses a binding approach which might or not be fully functional (didn't test)
I want to format a number in GWT LongBox For exmple: I have a number : 2,134, I don't want show thousand delimiter(,) here and want to show like 2134.
How can we implement this in GWT ?
Thanks
The best option is to implement your own LongBox widget:
public class MyLongBox extends ValueBox<Long> {
public MyLongBox() {
super(Document.get().createTextInputElement(),
new AbstractRenderer<Long>() {
public String render(Long l) {
return l == null ? "" : l.toString();
}
},
LongParser.instance());
}
}
MyLongBox lb = new MyLongBox();
lb.setValue(2134l);
RootPanel.get().add(lb);
But if you cannot change your ui, you could change the global decimalFormat variable.
private static native void changeCachedDecimalFormat(NumberFormat f) /*-{
#com.google.gwt.i18n.client.NumberFormat::cachedDecimalFormat = f;
}-*/;
changeCachedDecimalFormat(NumberFormat.getFormat("###0"));
LongBox lb = new LongBox();
lb.setValue(2134l);
RootPanel.get().add(lb);
In the doc of GWT's NumberFormat class (http://google-web-toolkit.googlecode.com/svn/javadoc/1.5/com/google/gwt/i18n/client/NumberFormat.html) I read:
"The prefixes, suffixes, and various symbols used for infinity, digits, thousands separators, decimal separators, etc. may be set to arbitrary values, and they will appear properly during formatting. However, care must be taken that the symbols and strings do not conflict, or parsing will be unreliable. For example, the decimal separator and thousands separator should be distinct characters, or parsing will be impossible."
My question is, how do I make sure that "." is used as thousands separator and "," as decimal separator independently of the user's locale settings?
In other words when I use the pattern "###,###,###.######" I want GWT to format the double value 1234567.89 always as "1.234.567,89" no matter what the user's locale is.
Solving this took some work. From the docs and source for NumberFormatter, it looks like only Locales can be used to set these values. They do say you can set the group separator but no such examples worked for me. While you might think the Java way to do this at the bottom would work since GWT emulates the DecimalFormat and DecimalFormalSymbols classes, they do not formally support them. Perhaps they will in the future. Further, they say in the LocaleInfo class that you can modify a locale, I found no such methods allowing this.
So, here is the Hack way to do it:
NumberFormat.getFormat("#,##0.0#").format(2342442.23d).replace(",", "#");
Right way, but not yet GWT supported:
Use the decimal formatter:
// formatter
DecimalFormat format= new DecimalFormat();
// custom symbol
DecimalFormatSymbols customSymbols=new DecimalFormatSymbols();
customSymbols.setGroupingSeparator('#');
format.setDecimalFormatSymbols(customSymbols);
// test
String formattedString = format.format(2342442.23d);
The output:
2#342#442.23
I just came across the same issue. I solved it like this:
public String formatAmount(Double amount) {
String pattern = "#,##0.00";
String groupingSeparator = LocaleInfo.getCurrentLocale().getNumberConstants().groupingSeparator();
String decimalSeparator = LocaleInfo.getCurrentLocale().getNumberConstants().decimalSeparator();
NumberFormat format = NumberFormat.getFormat(pattern);
return format.format(amount).replace(groupingSeparator, "'").replace(decimalSeparator, ".");
}
The way I've used is override the GWT's LocaleInfoImpl in this way:
Taking a look at LocaleInfo you can see it uses a private static instance LocaleInfo line 36 that build using GWT.create(LocaleInfoImpl.class)
Using GWT Deferred binding we can override the LocalInfoImpl by a custom implementation:
<replace-with class="your.app.package.to.CustomLocaleInfoImpl">
<when-type-is class="com.google.gwt.i18n.client.impl.LocaleInfoImpl" />
</replace-with>
Extend the LocaleInfoImpl in a similar way of this, just overriding the method getNumberConstant:
public class CustomLocaleInfoImpl extends LocaleInfoImpl {
#Override
public NumberConstants getNumberConstants() {
final NumberConstants nc = super.getNumberConstants();
return new NumberConstants() {
#Override
public String notANumber() {
return nc.notANumber();
}
#Override
public String currencyPattern() {
return nc.currencyPattern();
}
#Override
public String decimalPattern() {
return nc.decimalPattern();
}
#Override
public String decimalSeparator() {
return nc.decimalSeparator();
}
#Override
public String defCurrencyCode() {
return nc.defCurrencyCode();
}
#Override
public String exponentialSymbol() {
return nc.exponentialSymbol();
}
#Override
public String globalCurrencyPattern() {
return nc.globalCurrencyPattern();
}
#Override
public String groupingSeparator() {
return "#";//or any custom separator you desire
}
#Override
public String infinity() {
return nc.infinity();
}
#Override
public String minusSign() {
return nc.minusSign();
}
#Override
public String monetaryGroupingSeparator() {
return nc.monetaryGroupingSeparator();
}
#Override
public String monetarySeparator() {
return nc.monetarySeparator();
}
#Override
public String percent() {
return nc.percent();
}
#Override
public String percentPattern() {
return nc.percentPattern();
}
#Override
public String perMill() {
return nc.perMill();
}
#Override
public String plusSign() {
return nc.plusSign();
}
#Override
public String scientificPattern() {
return nc.scientificPattern();
}
#Override
public String simpleCurrencyPattern() {
return nc.simpleCurrencyPattern();
}
#Override
public String zeroDigit() {
return nc.zeroDigit();
}
};
}
}
I'm trying to create a page which is very similar to the Google Form creation page.
This is how I am attempting to model it using the GWT MVP framework (Places and Activities), and Editors.
CreateFormActivity (Activity and presenter)
CreateFormView (interface for view, with nested Presenter interface)
CreateFormViewImpl (implements CreateFormView and Editor< FormProxy >
CreateFormViewImpl has the following sub-editors:
TextBox title
TextBox description
QuestionListEditor questionList
QuestionListEditor implements IsEditor< ListEditor< QuestionProxy, QuestionEditor>>
QuestionEditor implements Editor < QuestionProxy>
QuestionEditor has the following sub-editors:
TextBox questionTitle
TextBox helpText
ValueListBox questionType
An optional subeditor for each question type below.
An editor for each question type:
TextQuestionEditor
ParagraphTextQuestionEditor
MultipleChoiceQuestionEditor
CheckboxesQuestionEditor
ListQuestionEditor
ScaleQuestionEditor
GridQuestionEditor
Specific Questions:
What is the correct way to add / remove questions from the form. (see follow up question)
How should I go about creating the Editor for each question type? I attempted to listen to the questionType value changes, I'm not sure what to do after. (answered by BobV)
Should each question-type-specific editor be wrapper with an optionalFieldEditor? Since only one of can be used at a time. (answered by BobV)
How to best manage creating/removing objects deep in the object hierarchy. Ex) Specifying answers for a question number 3 which is of type multiple choice question. (see follow up question)
Can OptionalFieldEditor editor be used to wrap a ListEditor? (answered by BobV)
Implementation based on Answer
The Question Editor
public class QuestionDataEditor extends Composite implements
CompositeEditor<QuestionDataProxy, QuestionDataProxy, Editor<QuestionDataProxy>>,
LeafValueEditor<QuestionDataProxy>, HasRequestContext<QuestionDataProxy> {
interface Binder extends UiBinder<Widget, QuestionDataEditor> {}
private CompositeEditor.EditorChain<QuestionDataProxy, Editor<QuestionDataProxy>> chain;
private QuestionBaseDataEditor subEditor = null;
private QuestionDataProxy currentValue = null;
#UiField
SimplePanel container;
#UiField(provided = true)
#Path("dataType")
ValueListBox<QuestionType> dataType = new ValueListBox<QuestionType>(new Renderer<QuestionType>() {
#Override
public String render(final QuestionType object) {
return object == null ? "" : object.toString();
}
#Override
public void render(final QuestionType object, final Appendable appendable) throws IOException {
if (object != null) {
appendable.append(object.toString());
}
}
});
private RequestContext ctx;
public QuestionDataEditor() {
initWidget(GWT.<Binder> create(Binder.class).createAndBindUi(this));
dataType.setValue(QuestionType.BooleanQuestionType, true);
dataType.setAcceptableValues(Arrays.asList(QuestionType.values()));
/*
* The type drop-down UI element is an implementation detail of the
* CompositeEditor. When a question type is selected, the editor will
* call EditorChain.attach() with an instance of a QuestionData subtype
* and the type-specific sub-Editor.
*/
dataType.addValueChangeHandler(new ValueChangeHandler<QuestionType>() {
#Override
public void onValueChange(final ValueChangeEvent<QuestionType> event) {
QuestionDataProxy value;
switch (event.getValue()) {
case MultiChoiceQuestionData:
value = ctx.create(QuestionMultiChoiceDataProxy.class);
setValue(value);
break;
case BooleanQuestionData:
default:
final QuestionNumberDataProxy value2 = ctx.create(BooleanQuestionDataProxy.class);
value2.setPrompt("this value doesn't show up");
setValue(value2);
break;
}
}
});
}
/*
* The only thing that calls createEditorForTraversal() is the PathCollector
* which is used by RequestFactoryEditorDriver.getPaths().
*
* My recommendation is to always return a trivial instance of your question
* type editor and know that you may have to amend the value returned by
* getPaths()
*/
#Override
public Editor<QuestionDataProxy> createEditorForTraversal() {
return new QuestionNumberDataEditor();
}
#Override
public void flush() {
//XXX this doesn't work, no data is returned
currentValue = chain.getValue(subEditor);
}
/**
* Returns an empty string because there is only ever one sub-editor used.
*/
#Override
public String getPathElement(final Editor<QuestionDataProxy> subEditor) {
return "";
}
#Override
public QuestionDataProxy getValue() {
return currentValue;
}
#Override
public void onPropertyChange(final String... paths) {
}
#Override
public void setDelegate(final EditorDelegate<QuestionDataProxy> delegate) {
}
#Override
public void setEditorChain(final EditorChain<QuestionDataProxy, Editor<QuestionDataProxy>> chain) {
this.chain = chain;
}
#Override
public void setRequestContext(final RequestContext ctx) {
this.ctx = ctx;
}
/*
* The implementation of CompositeEditor.setValue() just creates the
* type-specific sub-Editor and calls EditorChain.attach().
*/
#Override
public void setValue(final QuestionDataProxy value) {
// if (currentValue != null && value == null) {
chain.detach(subEditor);
// }
QuestionType type = null;
if (value instanceof QuestionMultiChoiceDataProxy) {
if (((QuestionMultiChoiceDataProxy) value).getCustomList() == null) {
((QuestionMultiChoiceDataProxy) value).setCustomList(new ArrayList<CustomListItemProxy>());
}
type = QuestionType.CustomList;
subEditor = new QuestionMultipleChoiceDataEditor();
} else {
type = QuestionType.BooleanQuestionType;
subEditor = new BooleanQuestionDataEditor();
}
subEditor.setRequestContext(ctx);
currentValue = value;
container.clear();
if (value != null) {
dataType.setValue(type, false);
container.add(subEditor);
chain.attach(value, subEditor);
}
}
}
Question Base Data Editor
public interface QuestionBaseDataEditor extends HasRequestContext<QuestionDataProxy>, IsWidget {
}
Example Subtype
public class BooleanQuestionDataEditor extends Composite implements QuestionBaseDataEditor {
interface Binder extends UiBinder<Widget, BooleanQuestionDataEditor> {}
#Path("prompt")
#UiField
TextBox prompt = new TextBox();
public QuestionNumberDataEditor() {
initWidget(GWT.<Binder> create(Binder.class).createAndBindUi(this));
}
#Override
public void setRequestContext(final RequestContext ctx) {
}
}
The only issue left is that QuestionData subtype specific data isn't being displayed, or flushed. I think it has to do with the Editor setup I'm using.
For example, The value for prompt in the BooleanQuestionDataEditor is neither set nor flushed, and is null in the rpc payload.
My guess is: Since the QuestionDataEditor implements LeafValueEditor, the driver will not visit the subeditor, even though it has been attached.
Big thanks to anyone who can help!!!
Fundamentally, you want a CompositeEditor to handle cases where objects are dynamically added or removed from the Editor hierarchy. The ListEditor and OptionalFieldEditor adaptors implement CompositeEditor.
If the information required for the different types of questions is fundamentally orthogonal, then multiple OptionalFieldEditor could be used with different fields, one for each question type. This will work when you have only a few question types, but won't really scale well in the future.
A different approach, that will scale better would be to use a custom implementation of a CompositeEditor + LeafValueEditor that handles a polymorphic QuestionData type hierarchy. The type drop-down UI element would become an implementation detail of the CompositeEditor. When a question type is selected, the editor will call EditorChain.attach() with an instance of a QuestionData subtype and the type-specific sub-Editor. The newly-created QuestionData instance should be retained to implement LeafValueEditor.getValue(). The implementation of CompositeEditor.setValue() just creates the type-specific sub-Editor and calls EditorChain.attach().
FWIW, OptionalFieldEditor can be used with ListEditor or any other editor type.
We implemented similar approach (see accepted answer) and it works for us like this.
Since driver is initially unaware of simple editor paths that might be used by sub-editors, every sub-editor has own driver:
public interface CreatesEditorDriver<T> {
RequestFactoryEditorDriver<T, ? extends Editor<T>> createDriver();
}
public interface RequestFactoryEditor<T> extends CreatesEditorDriver<T>, Editor<T> {
}
Then we use the following editor adapter that would allow any sub-editor that implements RequestFactoryEditor to be used. This is our workaround to support polimorphism in editors:
public static class DynamicEditor<T>
implements LeafValueEditor<T>, CompositeEditor<T, T, RequestFactoryEditor<T>>, HasRequestContext<T> {
private RequestFactoryEditorDriver<T, ? extends Editor<T>> subdriver;
private RequestFactoryEditor<T> subeditor;
private T value;
private EditorDelegate<T> delegate;
private RequestContext ctx;
public static <T> DynamicEditor<T> of(RequestFactoryEditor<T> subeditor) {
return new DynamicEditor<T>(subeditor);
}
protected DynamicEditor(RequestFactoryEditor<T> subeditor) {
this.subeditor = subeditor;
}
#Override
public void setValue(T value) {
this.value = value;
subdriver = null;
if (null != value) {
RequestFactoryEditorDriver<T, ? extends Editor<T>> newSubdriver = subeditor.createDriver();
if (null != ctx) {
newSubdriver.edit(value, ctx);
} else {
newSubdriver.display(value);
}
subdriver = newSubdriver;
}
}
#Override
public T getValue() {
return value;
}
#Override
public void flush() {
if (null != subdriver) {
subdriver.flush();
}
}
#Override
public void onPropertyChange(String... paths) {
}
#Override
public void setDelegate(EditorDelegate<T> delegate) {
this.delegate = delegate;
}
#Override
public RequestFactoryEditor<T> createEditorForTraversal() {
return subeditor;
}
#Override
public String getPathElement(RequestFactoryEditor<T> subEditor) {
return delegate.getPath();
}
#Override
public void setEditorChain(EditorChain<T, RequestFactoryEditor<T>> chain) {
}
#Override
public void setRequestContext(RequestContext ctx) {
this.ctx = ctx;
}
}
Our example sub-editor:
public static class VirtualProductEditor implements RequestFactoryEditor<ProductProxy> {
interface Driver extends RequestFactoryEditorDriver<ProductProxy, VirtualProductEditor> {}
private static final Driver driver = GWT.create(Driver.class);
public Driver createDriver() {
driver.initialize(this);
return driver;
}
...
}
Our usage example:
#Path("")
DynamicEditor<ProductProxy> productDetailsEditor;
...
public void setProductType(ProductType type){
if (ProductType.VIRTUAL==type){
productDetailsEditor = DynamicEditor.of(new VirtualProductEditor());
} else if (ProductType.PHYSICAL==type){
productDetailsEditor = DynamicEditor.of(new PhysicalProductEditor());
}
}
Would be great to hear your comments.
Regarding your question why subtype specific data isn't displayed or flushed:
My scenario is a little bit different but I made the following observation:
GWT editor databinding does not work as one would expect with abstract editors in the editor hierarchy. The subEditor declared in your QuestionDataEditor is of type QuestionBaseDataEditor and this is fully abstract type (an interface). When looking for fields/sub editors to populate with data/flush GWT takes all the fields declared in this type. Since QuestionBaseDataEditor has no sub editors declared nothing is displayed/flushed. From debugging I found out that is happens due to GWT using a generated EditorDelegate for that abstract type rather than the EditorDelegate for the concrete subtype present at that moment.
In my case all the concrete sub editors had the same types of leaf value editors (I had two different concrete editors one to display and one to edit the same bean type) so I could do something like this to work around this limitation:
interface MyAbstractEditor1 extends Editor<MyBean>
{
LeafValueEditor<String> description();
}
// or as an alternative
abstract class MyAbstractEditor2 implements Editor<MyBean>
{
#UiField protected LeafValueEditor<String> name;
}
class MyConcreteEditor extends MyAbstractEditor2 implements MyAbstractEditor1
{
#UiField TextBox description;
public LeafValueEditor<String> description()
{
return description;
}
// super.name is bound to a TextBox using UiBinder :)
}
Now GWT finds the subeditors in the abstract base class and in both cases I get the corresponding fields name and description populated and flushed.
Unfortunately this approach is not suitable when the concrete subeditors have different values in your bean structure to edit :(
I think this is a bug of the editors framework GWT code generation, that can only be solved by the GWT development team.
Isn't the fundamental problem that the binding happens at compile time so will only bind to QuestionDataProxy so won't have sub-type specific bindings? The CompositeEditor javadoc says "An interface that indicates that a given Editor is composed of an unknown number of sub-Editors all of the same type" so that rules this usage out?
At my current job I'm pushing to avoid polymorphism altogether as the RDBMS doesn't support it either. Sadly we do have some at the moment so I'm experimenting with a dummy wrapper class that exposes all the sub-types with specific getters so the compiler has something to work on. Not pretty though.
Have you seen this post: http://markmail.org/message/u2cff3mfbiboeejr this seems along the right lines.
I'm a bit worried about code bloat though.
Hope that makes some sort of sense!