Editor returns null values for empty strings after flush?

413 views
Skip to first unread message

P.G.Taboada

unread,
Aug 17, 2011, 7:27:00 PM8/17/11
to Google Web Toolkit
hi,

Not sure what I am doing wrong.

I did build a form with uibinder and I am populating a few TextBoxes
with an editor:

mydriver.initialize( this );
mydriver.edit( somebean );


... User edits data ...


changedBean = myeditor.flush();


When I look at the changedBean, all empty string properties became
null. Not what I or the database did expect.

I am using gwt build from trunk.

Brgds,

Papick

P.G.Taboada

unread,
Aug 17, 2011, 7:45:34 PM8/17/11
to Google Web Toolkit
From the ValueBoxBase<T> source:


/**
* Return the parsed value, or null if the field is empty.
*
* @throws ParseException if the value cannot be parsed
*/
public T getValueOrThrow() throws ParseException {
String text = getText();

T parseResult = parser.parse(text);

if ("".equals(text)) {
return null;
}

return parseResult;
}


Bug or feature? I feel very uncomfortable about having my empty
strings converted to null.

Comments?

P.G.Taboada

unread,
Aug 17, 2011, 8:28:05 PM8/17/11
to Google Web Toolkit
Well, the TextBoxBase, on the other hand, transforms any null value
into a empty string.

Not sure if this is good either, but as a matter o fact I do not know
who is turning my empty strings to null.

Flush? It should not.

Or?

Sudhakar Abraham

unread,
Aug 19, 2011, 9:09:35 AM8/19/11
to Google Web Toolkit

When You click the save button bean value should be set. It is
important that Bean class domain variable name should be same as
ui:field name in Editor class.

I have given below a simple program.
Hope it helps.

S.Abraham
www.DataStoreGwt.com

------------------- sample program
--------------------------------------

<<Editor class>>
public class MyEditorWorkFlow extends Composite {
interface Binder extends UiBinder<Widget, MyEditorWorkFlow>{}
Binder binder = GWT.create(Binder.class);
interface Driver extends SimpleBeanEditorDriver<MyData,MyEditor>
{}
private Driver driver = GWT.create(Driver.class);
@UiField (provided = true)
MyEditor editor;
MyData myData_ = null;
public MyEditorWorkFlow(MyData myData)
{
this.myData_ = myData;
editor = new MyEditor(myData_);
initWidget(binder.createAndBindUi(this));
edit(myData_);
}
@UiHandler("save")
void onSave(ClickEvent event)
{
MyData myData = driver.flush();
Window.alert("Name=" + myData.getName() );
}
public void edit(MyData myData)
{
driver.initialize(editor);
driver.edit(myData);
}
}

<<Bean class>>
public class MyData implements Serializable
{
public String name;

public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}

<<ui.xml>>
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui">
<g:FlowPanel>
<g:TextBox ui:field="name"/>
</g:FlowPanel>
</ui:UiBinder>

P.G.Taboada

unread,
Aug 19, 2011, 12:26:27 PM8/19/11
to Google Web Toolkit
Thank you for your reply. I took the time to create (a working ;)
small sample and it behaves like expected.

flushing is setting null on my beans properties.

I am logging the values to the console directly before the flush:

textbox value is ""
bean value is ""

==> calling flush

bean gets empty string fields set to null


I just built GWT from trunk. As I said, a simple editor sample here
works as expected.
The editor in my app does not. I know now it is my code, I am just too
blind to see it right now.






On 19 Aug., 14:09, Sudhakar Abraham <s.abra...@datastoregwt.com>
wrote:
Message has been deleted

P.G.Taboada

unread,
Aug 19, 2011, 1:57:08 PM8/19/11
to Google Web Toolkit
So here is my code, commented. Maybe someone spots my mistake, any
comments are welcome:

This is a simple app just to demo my problem.

The entry points creates the UI, the bean to be edited and populates
the properties with empty strings. The bean is then "displayed/
rendered/ edited" until save is pressed.

I do not change any field: I just run the gwt-app and click on save.
Hoping to get shit-in-shit-out, but I get null values out. If I
populate with anything other than empty strings it works perfectly.

Tested with gwt 2.3 and gwt built from trunk.

What am I missing?


public class SimpleEditorSample implements EntryPoint {

@Override
public void onModuleLoad() {

PersonEditor personEditor = new PersonEditor();

Person toBeEdited = new Person();

// populate with empty strings...
toBeEdited.setFullname("");
toBeEdited.setBeruf("");

personEditor.edit(toBeEdited);

RootLayoutPanel.get().add(personEditor);
}
}


The editor as its UI defined using uibinder, so here is the xml,
nothing special here:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui" >
<g:HTMLPanel>
<p>
<g:Label>Name</g:Label>
<g:TextBox ui:field="fullname" />
</p>
<p>
<g:Label>Beruf</g:Label>
<g:TextBox ui:field="beruf" />
</p>
<p>
<g:Button ui:field="save">Save</g:Button>
<g:Button ui:field="cancel">Cancel</g:Button>
</p>
</g:HTMLPanel>
</ui:UiBinder>

All the magic happens in the PersonEditor class. UIBinding and Editor
gwt-deferred binding magic first, injected uifields, initialization in
the standard constructor, edit and save methods:


public class PersonEditor extends Composite implements Editor<Person>
{

private static PersonEditorUiBinder uiBinder =
GWT.create(PersonEditorUiBinder.class);

interface PersonEditorUiBinder extends UiBinder<Widget,
PersonEditor> {
}

interface Driver extends SimpleBeanEditorDriver<Person,
PersonEditor> {
}

Driver driver = GWT.create(Driver.class);

@UiField
TextBox fullname;

@UiField
TextBox beruf;

@UiField
Button save;

public PersonEditor() {

initWidget(uiBinder.createAndBindUi(this));
driver.initialize(this);

}

public void edit(Person p) {
driver.edit(p);
log(p);
}

private void log(Person p2log) {
GWT.log("Fullname: " + p2log.getFullname());
GWT.log("Beruf: " + p2log.getBeruf());
}

@UiHandler("save")
void onSaveClick(ClickEvent e) {
GWT.log("Beruf UI field value: " + beruf.getValue());
GWT.log("going to flush...");
Person flushedPerson = driver.flush();
GWT.log("flushed...");
GWT.log("Beruf UI field value: " + beruf.getValue());
log(flushedPerson);
}

@UiHandler("cancel")
void onCancelClick(ClickEvent e) {
GWT.log("edit cancelled, go away...");
}
}


And finally, the bean being edited: a POJO, no constructor, two
properties mapped to two attributes:

public static class Person implements Serializable {

private String fullname;

private String beruf;

public String getFullname() {
return fullname;
}

public void setFullname(String name) {
this.fullname = name;
}

public String getBeruf() {
return beruf;
}

public void setBeruf(String beruf) {
this.beruf = beruf;
}

}



Jens

unread,
Aug 19, 2011, 3:44:02 PM8/19/11
to google-we...@googlegroups.com
I think your code seems fine and although I didn't dig through GWTs source code I found it totally legal to have null values for empty fields. I mean if you assume the object you edit goes (at some time) into the database then I think its desirable to store null (= nothing) into the database instead of "empty data".

So I do think its not a bug. Its just how the default TextBox editor implementation (The ValueBoxEditor: http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/gwt/editor/ui/client/adapters/ValueBoxEditor.java) handles empty data.

And the difference you see between ValueBoxBase<T> and TextBoxBase is because ValueBoxBase<T> operates on any data type T and if you do not know the type you have to return null in case of an empty String in the wrapped input element. Only subclasses of ValueBoxBase<T> can decide if something like an "empty T" exists and thats why TextBoxBase converts null back to an empty String. An IntegerBox for example cant do that (an empty Integer does not exist) and thus it returns null.

-- J.

Papick G. Taboada

unread,
Aug 19, 2011, 3:52:39 PM8/19/11
to google-we...@googlegroups.com
Agree. But the TextBox, as far as I saw in the code, turns nulls into empty strings. So it must be the editor framework. Unfortunately this one gets generated, so it is a black box to me right now. Have to dig deeper in the gwt code. 

The problem is: it would be easy to workaround the issue, but it would leave me not understanding what is wrong with my code. What did I do to break it? Variable names? Wrong order in initializing something? I don't know. 

Papick G. Taboada
+++
   pgt technology scouting GmbH

--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-web-toolkit/-/OeNePO10hssJ.
To post to this group, send email to google-we...@googlegroups.com.
To unsubscribe from this group, send email to google-web-tool...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-web-toolkit?hl=en.

Jens

unread,
Aug 19, 2011, 6:02:30 PM8/19/11
to google-we...@googlegroups.com
Ok after digging around in GWT's source code:

TextBox uses the generic ValueBoxEditor (see link in my previous answer) and does not have its own TextBoxEditor. The ValueBoxEditor knows the TextBox (but as ValueBoxBase<T>, because the ValueBoxEditor is generic) and calls ValueBoxBase.getValueOrThrow() to get the editor value. This call returns null for empty Strings as mentioned before. Thats why you got null for TextBoxes.

But I can see your point. TextBox overwrites ValueBoxBase<String>.getValue() and changes null to an empty String. But it does not overwrite ValueBoxBase<String>.getValueOrThrow(). So you end up having two methods that return the value but they have a different result for an empty String in the wrapped input element:

- getValue() returns an empty String (called by you in your example)
- getValueOrThrow() returns null (called by the editor framework)

In addition to these both methods you also have TextBox.getText() which returns the text via a DOM operation. This method never returns null for an empty TextBox so maybe thats why Google has overwritten getValue() to act the same way. Unfortunately they forgot getValueOrThrow() which is used by the Editor Framework. So I think its a bug in TextBoxBase: getText(), getValue() and getValueOrThrow() should all act the same way.

Maybe you can open an issue and ask to align both getValue methods so that they return the same for an empty string in the wrapped input element.

Hope that helps.

-- J.

P.G.Taboada

unread,
Aug 20, 2011, 3:45:25 AM8/20/11
to Google Web Toolkit

That was the missing piece for me: the editor framework uses
getvalueorthrow!

The issue is here:


http://code.google.com/p/google-web-toolkit/issues/detail?id=6713

P.G.Taboada

unread,
Aug 20, 2011, 7:20:49 AM8/20/11
to Google Web Toolkit
It looks like that it is enough to comment out the if statement in the
ValueboxBase it solved the issue for me. But I am not sure what
collateral damages that imposes to the request factory way of things.
The patch did not break any test (?!?), and the fact that the
TextBoxBase does "undo" this behavior is a bit scaring:

ValueBoxBase:

/**
* Return the parsed value, or null if the field is empty or parsing
fails.
*/
public T getValue() {
try {
return getValueOrThrow();
} catch (ParseException e) {
return null;
}
}

/**
* Return the parsed value, or null if the field is empty.
*
* @throws ParseException if the value cannot be parsed
*/
public T getValueOrThrow() throws ParseException {
String text = getText();

T parseResult = parser.parse(text);
/* patched to fix inconsistent behaviour
if ("".equals(text)) {
return null;
}
*/
return parseResult;
}


TextBoxBase:

@Override
public String getValue() {
String raw = super.getValue();
return raw == null ? "" : raw;

Jens

unread,
Aug 20, 2011, 7:51:11 AM8/20/11
to google-we...@googlegroups.com
You should patch TextBoxBase and not ValueBoxBase. Or create your own MyTextBox extends TextBox and overwrite the method.

ValueBoxBase itself is fine.. but TextBoxBase is not.


-- J.

Papick G. Taboada

unread,
Aug 20, 2011, 8:52:27 AM8/20/11
to google-we...@googlegroups.com
I'll do that as soon as I have notice that is a feature and not bug. For now patching is easy and I must not change all my UIs.

:-)


--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
Reply all
Reply to author
Forward
0 new messages