Autocomplete , how to display a default value (Model / TextRenderer)

1,306 views
Skip to first unread message

msl

unread,
Jun 4, 2012, 4:37:18 PM6/4/12
to wicket-jquery-ui
Hi Sebastien,

I started to play around with the AutoCompleteTextField but now I am
stuck. I´d like to edit an existing address record where the country
is already set. The list of countries (pojo with name and phoneprefix)
is loaded from the database.

// member variable
IModel<Country> selectedCountry;

//constructur
Country france=new Country("France","33"); // address.getCountry();
selectedCountry=new Model<Country>(france);

AutoCompleteTextField ac=new
AutoCompleteTextField<Country>("autocomplete", selectedCountry,new
TextRenderer<Country>("name"))

* initially the field value does not render the name - it seems that
selectedCountry.toString() is used
* when selecting a value from the list I would assume that the
selected model is a country but a String is returned?

What am I doing wrong?

Thanks
Markus

Sebastien Briquet

unread,
Jun 4, 2012, 5:28:14 PM6/4/12
to wicket-j...@googlegroups.com
Hello Markus,

To answer your 2 questions:


* initially the field value does not render the name - it seems that selectedCountry.toString() is used
> I just tested that.... And I should admit that you found the first bug ! :)


* when selecting a value from the list I would assume that the selected model is a country but a String is returned?
> You guessed well, the bean is returned. As the bean is wrapped into the model you supplied to the AC, your model (the member variable) is also up-to-date with the new selection.
Also note that AC is generic with <County> (in this case). If you do ac.getModelObject(), the object type is Country, it cannot be String.
Anyway, I did a test based on the sample on the demo site, it works well (at least for this second point). So, I don't know exactly what is wrong on your side but I attached TestPage.html & TestPage.java so you can compare.

Thanks & best regards,
Sebastien.
TestPage.java
TestPage.html

Sebastien Briquet

unread,
Jun 4, 2012, 6:24:18 PM6/4/12
to wicket-j...@googlegroups.com

msl

unread,
Jun 5, 2012, 7:59:57 PM6/5/12
to wicket-jquery-ui
Hi Sebastien,
thanks for your reply and your bugfix:-)

Sorry that I didn´t explain the second problem adequately (still
learning English^^).

I added a button as well as an onSubmit-method to the form:

final Form<String> form = new Form<String>("form")
{
@Override
protected void onSubmit()
{
super.onSubmit();
System.out.println(selectedCountry);
Object obj=selectedCountry.getObject();
System.out.println(obj);
}
};

Here the model of selectedCountry has been changed to a String and now
an exception is thrown - although in

protected void onSelected(AjaxRequestTarget target)
{
Country country = this.getModelObject();
....
}

the ModelObject is a Country which is correct.


Thanks for any help and best regards
Markus

Sebastien

unread,
Jun 6, 2012, 4:01:10 PM6/6/12
to wicket-j...@googlegroups.com
Hello Markus,

Just to be sure I did not misunderstood, the line "Country country = this.getModelObject();" return a Country, right ? You deliberately set a String as the model object in the meantime, right again ?

It should not be possible to set a String if you have an IModel<Country>...  But if you have an IModel<Object>, then you can set a String as the model object but "Country country = this.getModelObject();" can throw a ClassCastException... But in another hand, you wrote earlier that you had an IModel<Country>, that why I am a little bit lost... Could you please attach simple java & html files so I can have a look ?

Thanks in advance,
Sebastien.

msl

unread,
Jun 8, 2012, 4:30:20 AM6/8/12
to wicket-jquery-ui
Hi Sebastien,
in order to attach files I wrote an email two days ago but it seems
that the email got lost. (btw: how do I attach files to this message
when I am using the webinterface? - I will try to send the files to
you directly. In the meanwhile here are the code snippets :

protected void onSelected(AjaxRequestTarget target)
{
Country country = this.getModelObject(); <--------------------
everything is fine here !
target.add(feedbackPanel);
}
....

final Form<String> form = new Form<String>("form")
{
protected void onSubmit()
{
super.onSubmit();
System.out.println(model); <----------! but not here - now
the model contains a String and not a Country
Country country = this.getModelObject();
<-------------------- class cast Exception
}
};
this.add(form);

Thanks and best regards
Markus

Martin Grigorov

unread,
Jun 8, 2012, 4:33:01 AM6/8/12
to wicket-j...@googlegroups.com
Hi,

On Fri, Jun 8, 2012 at 11:30 AM, msl <markus....@googlemail.com> wrote:
> Hi Sebastien,
> in order to attach files I wrote an email two days ago but it seems
> that the email got lost. (btw: how do I attach files to this message
> when I am using the webinterface? - I will try to send the files to
> you directly. In the meanwhile here are the code snippets :
>
>  protected void onSelected(AjaxRequestTarget target)
>  {
>    Country country = this.getModelObject();  <--------------------
> everything is fine here !

#onSelected() is a method of the Autocompleter, right ?
So Autocompleter's model object type is Country.

>    target.add(feedbackPanel);
>  }
>  ....
>
>  final Form<String> form = new Form<String>("form")

Here you clearly state that the Form's model object type is String.

msl

unread,
Jun 8, 2012, 5:10:46 AM6/8/12
to wicket-jquery-ui
Hi Martin,

* your right, onSelected is a method of AutoCompleteTextField

* it doesn´t matter whether your write
final Form<Country> form = new Form<Country>("form")
or
final Form<String> form = new Form<String>("form")
or
final Form form = new Form("form")
or
final Form<IModel<Country>> form = new
Form<IModel<Country>>("form")

- Markus
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
JAVA:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.wicket.IClusterable;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;

import
com.googlecode.wicket.jquery.ui.form.autocomplete.AutoCompleteTextField;
import com.googlecode.wicket.jquery.ui.panel.JQueryFeedbackPanel;
import com.googlecode.wicket.jquery.ui.renderer.TextRenderer;

public class TestSebastienPage
{
private static final long serialVersionUID = 1L;

public TestSebastienPage()
{
// Model //
final IModel<Country> model = new Model<Country>();

// Form //
final Form form = new Form("form")
{
@Override
protected void onSubmit()
{
super.onSubmit();
System.out.println(model);
info(String.format("Selected country: %s (+%d)",
model.getObject().getName(), model.getObject().getPrefix()));
}
};
this.add(form);

Button button=new Button("buttonSave");
form.add(button);

// FeedbackPanel //
final FeedbackPanel feedbackPanel = new
JQueryFeedbackPanel("feedback");
form.add(feedbackPanel.setOutputMarkupId(true));

// Auto-complete //
form.add(new AutoCompleteTextField<Country>("autocomplete",
model, new TextRenderer<Country>("name"))
{
private static final long serialVersionUID = 1L;
@Override
protected List<Country> getChoices(String input)
{
List<Country> choices = new ArrayList<Country>();
int count = 0;
for (Country genre: COUNTRIES)
{
if
(genre.getName().toLowerCase().contains(input.toLowerCase()))
{
choices.add(genre);
if (++count == 20) { break; } //limits the
number of results
}
}
return choices;
}

@Override
protected void onSelected(AjaxRequestTarget target)
{
// Markus everything is fine here !
//Country genre = this.getModelObject(); //can be used
info(String.format("Selected country: %s (+%d)",
model.getObject().getName(), model.getObject().getPrefix()));
target.add(feedbackPanel);
}
});
}

// List of Countries //
static final List<Country> COUNTRIES = Arrays.asList(
new Country(33, "France"),
new Country(49, "Allemagne"));

// Bean //
static class Country implements IClusterable
{
private static final long serialVersionUID = 1L;

private final int prefix;
private final String name;

public Country(final int prefix, final String name)
{
this.prefix = prefix;
this.name = name;
}

public int getPrefix()
{
return this.prefix;
}

public String getName()
{
return this.name;
}
}
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
HTML:
<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
<head>
<wicket:head>
<title>Wicket - jQuery UI: test page</title>
<style type="text/css">
.ui-autocomplete {
max-height: 200px;
overflow-y: auto;
overflow-x: hidden;
padding-right: 20px;
}
</style>

</wicket:head>
</head>
<body>
<wicket:extend>
<div id="wrapper-panel-frame" class="ui-corner-all">
<form wicket:id="form">
<div>Select a country: (containing any char)</div>
<br/>
<input wicket:id="autocomplete" type="text" size="30"></
input><br/>
<br/>
<div wicket:id="feedback" style="width: 360px;"></div>
<br/>
<button class="saveButton" type="submit"
wicket:id="buttonSave" ><wicket:message key="button.save"/></button>

</form>
</div>
</wicket:extend>
</body>
</html>



Markus Lutz

unread,
Jun 6, 2012, 6:05:46 PM6/6/12
to wicket-j...@googlegroups.com
Hi Sebastien,

I attached two files - actually your test files with little changes
(added a button to the form
and overwrote form.onSubmit where my problem is located.


protected void onSelected(AjaxRequestTarget target)
{
Country country = this.getModelObject();
<-------------------- everything is fine here !
target.add(feedbackPanel);
}



final Form<String> form = new Form<String>("form")
{
protected void onSubmit()
{
super.onSubmit();
System.out.println(model); <----------------------------
!!! My problem is here ! I would expect a Model with Country but it
is a String
}
};
this.add(form);



Thanks and best regards
Markus



TestSebastienPage.html
TestSebastienPage.java

Martin Grigorov

unread,
Jun 8, 2012, 9:00:15 AM6/8/12
to wicket-j...@googlegroups.com
final IModel<Country> model = new Model<Country>();

Put a breakpoint in Model#setObject() and see who calls it with a
String parameter

Sebastien Briquet

unread,
Jun 8, 2012, 9:01:12 AM6/8/12
to wicket-j...@googlegroups.com
Hello Markus (& hello Martin) !

I will have a look at this tonight, but according the the code it looks like to be good now... Is the problem located in the Form#onSubmit ?
In the meantime, may I suggest you to generize the form, like Form<Country>, or Form<Void> if you do not wish to use a form's model. It will reduce potential risk of errors at compilation time (if you keep Form without the generic, the generic will be object)...

Best regards,
Sebastien.

msl

unread,
Jun 8, 2012, 10:56:20 AM6/8/12
to wicket-jquery-ui
Hi Sebastien,
yes the problem is located in the Form#onSubmit.
Best regards
Markus

Sebastien Briquet

unread,
Jun 8, 2012, 8:54:07 PM6/8/12
to wicket-j...@googlegroups.com
Hello Markus,

I've got it. I should have realized earlier because it is the same behavior than DatePicker & Co (and I treated with these components only some days ago!)

So, what's happened?

The general purpose:
AutoCompleteTextField is a TextField and it is basically designed to work with String (which is natural for a "Text"Field)
But TextField is also generic so it allows you to deals with beans. But, there is a condition: it needs to know how to do the conversion, by providing an IConverter. The converters converts from object to string and string to object (that's this conversion we need).

Providing a converter can be done in 2 ways:
- overriding AutoCompleteTextField#getConverter() so it returns the appropriate IConverter
- registering the converter at application level so you do not need to override #getConverter().
More info here: https://cwiki.apache.org/WICKET/using-custom-converters.html

The specific purpose:
By using the AutoCompleteTextField with a custom bean - and if you need to use the form component (meaning, not only use the #onSelected method, but posting the form) - you need to provide the converter. But... I missed something important in the constructor: the ability to provide the class type! Without the class type, there is no way to use a Converter. There is still a workaround which is to call autocomplete.setType(Country.class) manually. I am attaching the working sample page (with converter & workaround)....

Anyway, this is an issue. 2 points for you :)
I will correct it asap.

Thanks & best regards,
Sebastien.
TestSebastienPage.java

msl

unread,
Jun 9, 2012, 6:51:52 AM6/9/12
to wicket-jquery-ui
Hi Sebastien,

no points for me:-) ... I am happy that I can use your library -
thanks for your work!

Best Regards
Markus

Sebastien Briquet

unread,
Jun 9, 2012, 7:43:03 AM6/9/12
to wicket-j...@googlegroups.com
Hello Markus,

You're welcome!

I realized afterward that, as the ACTF already hosts the typed-up-to-date model object (because of the user selection), there is no need to provide an explicit IConverter. It can be handled by the ACTF itself. The only condition is to provide the class-type to the constructor so the internal converter can be called.

Note that, If the user does not select a value within the available typed choices (custom user input), then the returned model object will be null. This remains logical has the ACTF is designed to works with beans and we attend to retrieve a bean. If we want to deal with user's custom input, then the ACTF should be generized with String (and the type not supplied to the constructor).

Fixed in 1.2.1-SNAPSHOT:
https://github.com/sebfz1/wicket-jquery-ui/commit/c0206cd8d6f5f1583c71ced18ae5e14679f89b4e

The updated usage can be found here:
https://github.com/sebfz1/wicket-jquery-ui/blob/master/wicket-jquery-ui-samples/src/main/java/com/googlecode/wicket/jquery/ui/samples/pages/autocomplete/ConverterAutoCompletePage.java

Best regards,
Sebastien.

msl

unread,
Jun 10, 2012, 7:37:52 PM6/10/12
to wicket-j...@googlegroups.com
Hi Sebastien,
everything works fine ... almost:)

Now I needed the custom string when the user entered a country which didn´t exist (no ACTF-Selection was made). So I picked up the inputvalue during form validation with the actfCountry.getRawInput().
This worked pretty good until I used the ACTF within a modal dialog. Now rawInput is not set anymore - maybe because of different form(ajax) handling ? Any idea ? I attached a simple example.

Best Regards
Markus

seb1.zip

Sebastien Briquet

unread,
Jun 11, 2012, 5:28:54 AM6/11/12
to wicket-j...@googlegroups.com
Hello Markus,

In UserDialog:

            public void validate(Form<?> form)
            {
                 countryinput=actfCountry.getInput();
//                countryinput=actfCountry.getRawInput();
            }

From the quick test I did from you sample, it seems to work:
selected country :null
country input :England
country prefix :null

Best regards,
Sebastien.

msl

unread,
Jun 11, 2012, 1:22:58 PM6/11/12
to wicket-j...@googlegroups.com
great:-)
Thx Sebastien
Best Regards
Markus
Reply all
Reply to author
Forward
0 new messages