PropertyFormatter

Hi There,

I am struggling with the
PropertyFormatter
class. Anyone knows where to find a complete working example … I hoped the sampler had one …

Nicolas

Hi,

here is the simple example I’ve used, it seems that it does not work because of the FieldFactory:


public class VaadintestApplication extends Application {

	BeanItem bi = new BeanItem(new Manager("Eddy", 20.0d));

	@Override
	public void init() {
		Window mainWindow = new Window("Vaadintest Application");

		Form form = new Form();
		form.setFormFieldFactory(new MyFieldFactory());
		form.setItemDataSource(bi);
		
		mainWindow.addComponent(form);

		mainWindow.addComponent(new Button("Test"));

		setMainWindow(mainWindow);
	}

	class MyFieldFactory extends DefaultFieldFactory {
		@Override
		public Field createField(Item item, Object propertyId, Component uiContext) {

			// Identify the fields by their Property ID.
			String pid = (String) propertyId;
			if (pid.equals("name")) {
				return new TextField("Name");
			} else if ("salary".equals(propertyId)) {				
				TextField tf = new TextField("Salary");
				tf.setPropertyDataSource(new PropertyFormatter(item.getItemProperty(propertyId)) {
					public String format(Object value) {
						return "1" + ((Double) value).toString() + "00000";
					}

					public Object parse(String formattedValue) throws Exception {
						return Double.parseDouble(formattedValue);
					}
				});
				return tf;
			}

			// Let BaseFieldFactory create other possible fields.
			return super.createField(item, propertyId, uiContext);
		}
	}
}

When using a FieldFactory, the Field returned by the createField() method is reassigned with a new property so the PropertyFormatter is replaced, from the Vaadin Form class:


       // Adds all the properties to this form
        for (final Iterator i = propertyIds.iterator(); i.hasNext();) {
            final Object id = i.next();
            final Property property = itemDatasource.getItemProperty(id);
            if (id != null && property != null) {
                final Field f = fieldFactory.[b]
createField(itemDatasource, id,
                        this);
[/b]
                if (f != null) {
                    [b]
f.setPropertyDataSource(property);
[/b]
                    addField(id, f);
                }
            }
        }

Would it be a better idea to use the PropertyFormatter at the BeanItem level, maybe creating a FormattedBeanItem class or something … What do you think ?

Nicolas

You’re right about creating your own Item class. That is one possible solution. The point is, that the FieldFactory isn’t meant to provide content to the data, the Form already has the data in its Item. It only uses the FieldFactory to render the representation for the data - the field.

I’m not sure about the best solution here, but I suggest you start by looking at the source code of MethodProperty and BeanItem and see how they work. You could also think about creating your own fields that represent the data the way you want to.

Hi,

thanks for your answer, I finally create an EnhancedBeanItem that delegates the creation of the contained Properties to a PropertyFactory (given as argument when building the EnhancedBeanItem) instead of building MethodProperty objects.
This way the factory may return PropertyFormatter.

Nicolas

Hi

I know that this thread is quite old but I run into similar problem.

I want to use BeanItem and my custom FormFieldFactory with PropertyFormatter for some/all fields. When I set PropertyFormatter in FormFieldFactory it is overwrite by Form.setItemDataSource method.

As Risto wrote, Form already has the data and to represent them, FormFieldFactory is used. I propose this fix to Form.setItemDataSource method:

// Adds all the properties to this form
for (final Iterator i = propertyIds.iterator(); i.hasNext();) {
    final Object id = i.next();
    final Property property = itemDatasource.getItemProperty(id);
    if (id != null && property != null) {
        final Field f = fieldFactory.createField(itemDatasource, id,
                this);
        if (f != null) {
            [b]
if( f.getPropertyDataSource() == null ) {
                f.setPropertyDataSource(property);
            }
[/b]
            addField(id, f);
        }
    }
}

So FieldFormatter must/should be provided by FormFieldFactory. If it’s not then Form class “fixes” it by setting default one got from Item. Formatter is linked with FormFieldFactory which is linked with representing data. So the main idea isn’t broke.

Now if I want to do it so, that FieldFactory provides formatters for its fields I should reimplement Form.setItemDataSource method what is “impossible” because itemDatasource field is private and setter for this field invokes…setItemDataSource method :slight_smile:

Created these tickets:


4445


4446

I’m suffering from the same problem as Radek. I have a generic Form which I use to display my domain items. I pass a FormFieldFactory to it in the constructor, so it knows how to display the properties.

One field of a domain object is an Area object. If my Form is readOnly, I want to display the Area abbreviation in a TextField. If the Area can be modified, I want to use a ComboBox with the current Area preselected.

Here’s my code:


        if(Area.class.isAssignableFrom(property.getType()))
        {
        	Area area = (Area)property.getValue();
        	if (editable) {
        		f = createAreaComboBox(area);
        		f.setRequired(true);
        	}
        	else {
        		f = new TextField();
                        f.setValue(area.getAbbreviation());
			}
        }

All else is fine, but the value reverts to using toString() after the code that Radek has highlighted. I then implemented a propertyFormatter which I set in the else, but the same thing happens.

The tickets don’t really address this issue, with Tables I can always use a ColumnGenerator to determine what to show. With Forms, I’ve found no such way.

The PropertyFormatter does not appear to work quite as expected.

I tried to use it in my
FillEditor.java
, but ran into problems. It’s a bit annoying that you can’t use it inside the field factory as it gets reset after it. In fact, the field property source seems to be reset also when setVisibleItemProperties() is called, so you have to set it after that.

There also appears to be a bug (
#4484
) that the format() is called right in the constructor of PropertyFormatter, so beware that the constuctor of your subclass is not executed before format() called for the first time…

The bug #4484 also contains some information on our problem: “it’s annoying that the property formatter can not be set in the field factory and must be called after setVisibleItemProperties() is called.”

I also don’t understand why visible properties of a Form have to be changed when the object type passed to it remains the same. I would think that’s the most usual case: a Form is used to show a certain type of object and sometimes (selection on a Table, etc.) the object changes, but the Form doesn’t alter.

However, with Vaadin, I have to do this when changing the data object:


        Collection visibleProperties = getVisibleItemProperties();
        setItemDataSource(newObject);
        setVisibleItemProperties(visibleProperties);

And even that doesn’t work because the PropertyFormatter get’s erased :frowning:

I promise to say 5 good things of Vaadin to fellow developers at our office if this gets fixed :smiley:

I understand form is a place to input data and not display them, but I would agree with Radek fix suggestion. I don’t think there would be any side effects to patch the Form class this way. Is there a chance to see this fixed in Vaadin 6.5.x ?

I have to make a Vaadin presentation at my office, I don’t want neither to mention this limitation to my co-workers :wink:

Hi:

Was there a final solution for this thread?..since I can’t use the property formatter in the field factory I’m trying to use it after that…I just want to add formatting to a double field in the item…

DecimalFormat df =new DecimalFormat("#,###,###,##0.0");

I tried everything and didn’t make it to work…

thanks!!!

Not sure if it helps but there is an example of some PropertyFormatters
here
.

worked perfect!!!..thank you very much…

Just a little note: the example mentioned by Henri seems to have a bug. When I leave the sugarContent blank and click the Ok-button a com.vaadin.data.Property$ConversionException is thrown.

Hi,

That’s an odd bug. Looks like it works at first, but gives the error if you empty the field after having a numeric value for it. The property formatter in the example checks for empty input, so the problem isn’t there. The error is an “empty String” error that occurs in the framework… It’s possible that it’s even a bug in the framework, although I’m not sure without closer inspection, and it might be already fixed, as those are rather old Vaadin 6 examples.

Anyhow, PropertyFormatter is deprecated in Vaadin 7 and replaced by Converter, so unless you are using Vaadin 6, I recommend using Converter.

Pittifully I’m still bound to Vaadin 6 and still struggling with the PropertyFormatter. But maybe I’m using the wrong objects for what I’ve in mind:

Besides that in my
BeanItem
the property class is a domain class (in my case
Amount
) I would like that when a Field loses its focus on a Form the user input gets formatted. To achieve that I created
AmountField
(see below).
I found out that
writeThough
has to be set to
true
, else parsing is done only when the Form is committed.

Is there a better solution for this, in which
writeThrough
can be set to false?

(I’ve been reading about the addon
CustomField
but couldn’t make up how to use it for this problem. In the example below the
PropertyConverter
is used instead of
PropertyFormatter
. But that doesn’t make any difference in my experience. At least it makes proper use of generics.)

public class AmountField extends TextField {
    public AmountField () {
        addValidator (new AmountValidator ());
        }

    @Override
    public void setPropertyDataSource (Property newDataSource) {
        super.setPropertyDataSource (new AmountFormatter (newDataSource, false));
        }
    }
[/code][code]
public class AmountFormatter extends PropertyConverter<Amount, String> {
    private final DecimalFormatSymbols symbols;
    private final DecimalFormat        nf;

    public AmountFormatter (Property propertyDataSource) {
        super (propertyDataSource);

        symbols = new DecimalFormatSymbols ();
    
        nf = new DecimalFormat ("#,##0.00", symbols);
        nf.setParseBigDecimal (true);
        }

    @Override
    public String format (Amount amount) {
        return amount.toDisplayString ();
        }

    @Override
    public Amount parse (String fc) throws ConversionException {
        Amount out;

        if (StringUtils.isBlank (fc)) {
            out = Amount.getBlankInstance ();
            }
        else {
            try {
                BigDecimal number = (BigDecimal)nf.parse (fc);
                out = Amount.valueOf (number);
                }
            catch (ParseException e) {
                throw new ConversionException (e);
                }
            }

        return out;
        }
    }