Converting a bean path to a simple string property in Ext GWT grid - gwt

I am working on an Ext GWT 3 (beta) application.
I am trying to display a simple value of a dependent bean in a Grid.
My data beans look like this:
public class MyBean {
private String content;
private MyOtherBean otherBean;
// getters and setters here...
}
public class MyOtherBean {
private String otherBeanContent;
// getter and setter here...
}
The PropertyAccess looks like this:
interface MyBeanProperties extends PropertyAccess<MyBean> {
ModelKeyProvider<MyBean> key();
ValueProvider<MyBean, String> content();
ValueProvider<MyBean, MyOtherBean> otherBean();
}
With the corresponding ColumnConfiguration, my grid now displays one column with content of MyBean and one column with MyOtherBean's toString().
But I want to display MyOtherBean.otherBeanContent instead (without changing MyOtherBean's toString()).
I think I need some kind of value converter and register it for the column? Or am I taking the wrong approach here?

This did the trick:
columnConfig.setCell(new PropertyDisplayCell<MyOtherBean>(new PropertyEditor<MyOtherBean>() {
#Override
public MyOtherBean parse(CharSequence text) throws ParseException {
return otherBean.setOtherBeanContentFromText(text);
}
#Override
public String render(MyOtherBean otherBean) {
return otherBean == null ? "" : otherBean.getOtherBeanContent();
}
}));

Related

Is it possible to pass arguments in the expression of a PropertyModel?

I have a model object that has a getter/setter that accepts a String.
public String getStringValue(String key)
I need to know if it is possible to use that getter with a PropertyModel and if so how do I do it? An example might look something like this:
new PropertyModel<String>(myObj, "StringValue[key]");
There isn't built in way to do it. But you can define your own Wicket Model to do it via reflection.
For example:
public class FunctionReflectionReadOnlyModel<T, R> extends AbstractReadOnlyModel<T> {
private Object object;
private String functionName;
private R key;
private Class<R> keyClass;
public FunctionReflectionReadOnlyModel(Object object, String expression, Class<R> keyClass) {
this.object = object;
this.functionName = getFunctionName(expression);
this.key = getKey(expression);
this.keyClass = keyClass;
}
#Override
public T getObject() {
try {
Method method = object.getClass().getMethod(functionName, keyClass);
return (T)method.invoke(object, key);
} catch (Exception ex) {
//process exception
return null;
}
}
}
You just need implement getFunctionName(String expression) and getKey(String expression) on your needs.
But I think that is better use another variant. It's not particularly what you ask, but it is typified. Also required Java 8.
public class FunctionWithKeyReadOnlyModel<T, R> extends AbstractReadOnlyModel<T> {
private Function<R, T> function;
private R key;
public FunctionWithKeyReadOnlyModel(Function<R, T> function, R key) {
this.function = function;
this.key = key;
}
#Override
public T getObject() {
return function.apply(key);
}
}
And then you can use it like this:
new FunctionWithKeyReadOnlyModel(obj::getStringValue, "key");
I've read about usage only PropertyModel too late. In this case you can inherit your class from PropertyModel and change getModel/setModel like in example FunctionReflectionReadOnlyModel. So you don't need change other classes API. But if you want all features of PropertyModel (nested objects) you need implement it.
As answered by #merz this is not supported by Wicket's PropertyModel, actually by PropertyResolver.
PropertyResolver supports such access if you use a java.util.Map:
public Map<String, String> getProperty() {return theMap;}
Check org.apache.wicket.core.util.lang.PropertyResolver's javadoc.

Custom Renderer in GWT

I'm trying to create a widget that will render its associated value in a format that is not the same as the native value. For example, if the value (in the database) is "abcde" I want to show "ab.cd.e" on the screen, and if the user types "abcde" I would also want to show "ab.cd.e". If the user types "ab.cd.e" then I would want to store just "abcde" in the database. I am doing this within the GWT editor framework. I have attempted to use the advice from this answer: Converting String to BigDecimal in GWT, but I can't get it to work. Here's what I have in the UiBinder file:
<g:TextBox ui:field='myTextBox' width='300px'/>
And in the associated Java unit:
#UiField
TextBox myTextBox;
...
initWidget(binder.createAndBindUi(this));
new MyValueBox(myTextBox);
And here's the definition of the MyValueBox widget:
public class MyValueBox extends ValueBox<String> {
//=========================================================================
public static class MyRenderer extends AbstractRenderer<String> {
private static MyRenderer _instance;
private static MyRenderer instance() {
if (_instance == null) {
_instance = new MyRenderer();
}
return _instance;
}
#Override
public String render(final String text) {
// validation is required before doing this!
return text.substring(0, 2) + "." + text.substring(2, 4) + "."
+ text.substring(4);
}
}
//=========================================================================
public static class MyParser implements Parser<String> {
private static MyParser _instance;
private static MyParser instance() {
if (_instance == null) {
_instance = new MyParser();
}
return _instance;
}
#Override
public String parse(final CharSequence text) throws ParseException {
return "parsed string";
}
}
//=========================================================================
public MyValueBox(final TextBox valueBox) {
super(valueBox.getElement(), MyRenderer.instance(), MyParser.instance());
}
}
As you can see, I'm trying to wrap the TextBox that was created using UiBinder, but I don't see any effect from this. I know that I'm missing something very simple, and that there is a much easier way to accomplish this, but I'm stumped. Thank you for any suggestions!
--Edit--
I eventually decided to use a CellWidget, which had the added advantage that I can use this code in a cell widget (e.g., a DataGrid), in addition to using it on a panel. I have documented my solution here: GWT: A Custom Cell Example
You are missing to declare your custom Widget in the UIBinder. You need to tie the package to the xml declaration, adding yours to the standard one (called 'g'):
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder' xmlns:g='urn:import:com.google.gwt.user.client.ui' xmlns:myurn='urn:import:mypackage'>
Then you should use your declared urn, and the name of your class when declaring your TextBox in the UIBinder:
<myurn:MyValueBox ui:field='myTextBox' width='300px'/>
======EDIT=====
You should extend ValueBoxBase instead of wrapping TextBox, that way you will get control over the Renderer and the Parser as you intend, now you will be able to use your custom box as a widget from within the UIBinder:
public class CustomText extends ValueBoxBase<String>
{
public CustomText() {
super(Document.get().createTextInputElement(),CustomRenderer.instance(),
CustomParser.instance());
}
private static class CustomRenderer extends AbstractRenderer<String>
{
private static CustomRenderer INSTANCE;
public static CustomRenderer instance() {
if (INSTANCE == null) {
INSTANCE = new CustomRenderer();
}
return INSTANCE;
}
#Override
public String render(String text)
{
return "rendered string";
}
}
private static class CustomParser implements Parser<String>
{
private static CustomParser INSTANCE;
public static CustomParser instance() {
if (INSTANCE == null) {
INSTANCE = new CustomParser();
}
return INSTANCE;
}
#Override
public String parse(CharSequence text) throws ParseException
{
return "parsed string";
}
}
}

How to bind a list of objects to ListBox in GWT?

I want to bind my services to ListBox but I can't bind it.
//Service class
public class Service {
private String serviceName;
public Service(String serviceName) {
this.serviceName = serviceName;
}
public String getServiceName() {
return serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
}
// SignUpBean class
public class SignUpBean {
private List<Service> services;
public List<Service> getServices() {
return services;
}
public void setServices(List<Service> services) {
this.services = services;
}
}
The following is my Main Editor
public class SignUpEditor extends SimplePanelimplements Editor<SignUpBean> {
public ListBox services;
public void SignUpEditor (){
services.addItem("Service1");
services.addItem("Service2");
setWidget(services);
}
}
I am not getting any error but I think I have to use ListEditor or CompositeEditor. But i don't know about it. Can anyone suggest how to bind Service to ListBox
ListBox suppose to be a LeafValueEditor. Conceptually we don't edit the list in the ListBox. Rather We select a value from the list which will be displayed in the selection. So, ListBox holds only one value and thus ListBox must be leaf value Editor.
Currently there is no GWT support to directly bind the list to ListBox. We have to write an adapter which extends ListEditor ( Refer HasDataEditor class for more detail ). This approach is strange.
Simple approach is to write an adapter which implements LeafValueEditor< List < String > >. In adapter's setValue method we should iterate over the list and call listBox.addItem for each value in the list.
I don't recommend either approaches simply because ListBox's LIST is NON EDITABLE and only VALUE is EDITABLE. I recommend doing addItems manually without using Editors.
UPDATED : HasDataAdapter is an example ListEditor. Refer that implementation. You may get some idea.

GWT ValueListBox, Renderer and ProvidesKey

How to implement a GWT ValueListBox inside an Editor with a specific list of objects, my code:
...
#UiField(provided = true)
#Path("address.countryCode")
ValueListBox<Country> countries = new ValueListBox<Country>(
new Renderer<Country>() {
#Override
public String render(Country object) {
return object.getCountryName();
}
#Override
public void render(Country object, Appendable appendable)
throws IOException {
render(object);
}
},
new ProvidesKey<Country>() {
#Override
public Object getKey(Country item) {
return item.getCountryCode();
}
});
...
The Country class
public class Country {
private String countryName;
private String countryCode;
}
But, during the GWT compilation I'm getting this error:
Type mismatch: cannot convert from String to Country
The problem is that you are trying to edit the address.countryCode (looking at the path annotation) with editor for Country.
To make this work, you should change the path to address.country and do the assignment of the address.countryCode after editorDriver.flash(). Something like:
Address address = editorDriver.flush();
address.setCountryCode(address.getCountry().getCountryCode());
To support this, the Address class should have the Country object as property.
You may have assumed that the ValueListBox will work like classical select where the key is assigned to the property. Here the whole object is assigned. So in your case Country object can not be assigned to address.countryCode and vice-versa.
Btw. you can correct the renderer (like the code below) and take care of null objects as arguments in the Renderer and Key Provider.
new Renderer<Country>() {
...
#Override
public void render(Country object, Appendable appendable)
throws IOException {
appendable.append(render(object));
}
...
}

Custom label for a SELECT in Spring Roo

I am starting with Spring Roo. In my project, I have to entities with a one-to-many relation. In my controller, when I edit one entity, I get an HTML SELECT to choose one of the other entity. I'd like to have a custom label in this SELECT.
I tried to register a Converter in my ApplicationConversionServiceFactoryBean :
public class ApplicationConversionServiceFactoryBean extends
FormattingConversionServiceFactoryBean {
#Override
protected void installFormatters(FormatterRegistry registry) {
super.installFormatters(registry);
// Register application converters and formatters
registry.addConverter(getApplicationConverter());
}
public Converter<Application, String> getApplicationConverter() {
return new Converter<Application, String>() {
#Override
public String convert(Application source) {
return "toto" + source.getName();
}
};
}
}
This doesnt seem to work, the SELECT is still filled with what looks like the result of Application.toString().
What am I missing ?
I did find a solution. I still dont know if it is the right one ...
public class ApplicationConversionServiceFactoryBean extends
FormattingConversionServiceFactoryBean {
static class ApplicationConverter implements Converter<Application, String> {
#Override
public String convert(Application source) {
return "toto" + source.getName();
}
}
#Override
protected void installFormatters(FormatterRegistry registry) {
super.installFormatters(registry);
// Register application converters and formatters
registry.addConverter(new ApplicationConverter());
}
}
This seems to work for the labels in a SELECT. Is it the recommended way ?