Setting value of TextField without refreshRowCache / repaint

Hi there,

I have a question regarding TextFields in Vaadin Tables. My UI has a table with several columns, bound to model objects. Some of these columns are editable, others are calculated from these editable fields. All data is stored in an EventListContainer (glazedList). Why calls the .setValue of TextField (which is called, when the user changed the value and field is losing focus) refreshRowCache automatically and is that really necessary? Is there no way to avoid this time consuming method? When the calculation of the other (non-editable) fields has finished, I can update their values without calling refreshRowCache…

Thanks
Patric

If a column is not editable, vaadin just outputs the text. It is not a Label, so it is not bound to the property and will not update when the property is changed.

One way around this is to override Table.createField.

Table calls createField for each cell. If this returns null, it calls Table.formatPropertyValue to get just the text.

Unfortunately, createField requires you to return a Field, so if you want to return a Label you have to wrap it in a CustomField.

Btw; You won’t solve this by moving to Grid. It has the same kind of problems with its editor-row.

Thanks for Your reply.

As far as I understand the implementation my data is bound to the property by using GlazedLists. What I don’t understand is, how to update the data model trough a property. I just made an easy example. What I expected was that the “Manipulate” Button would change the data, but RefreshRowCache method of Vaadin Table shouldn’t be called. The Java-Doc says:

/** * Discards and recreates the internal row cache. Call this if you make * changes that affect the rows but the information about the changes are * not automatically propagated to the Table. * <p> [color=#FF0000] * Do not call this e.g. if you have updated the data model through a * Property. These types of changes are automatically propagated to the * Table. [/color] * <p> * A typical case when this is needed is if you update a generator (e.g. * CellStyleGenerator) and want to ensure that the rows are redrawn with new * styles. * <p> * <i>Note that calling this method is not cheap so avoid calling it * unnecessarily.</i> * * @since 6.7.2 */ public void refreshRowCache() { resetPageBuffer(); refreshRenderedCells(); } So how can I update my data model through a property? Any ideas? Obviously not by getContainerProperty.setValue?

    private EventList<Person> data;

    private Table table;

    private EventListContainer<Person> container;

    @Override
    protected void initViewComponents() {
        this.setSizeFull();

        data = makeEventList();

        PropertyHandler<Person> handler = makePropertyHandler();

        // If sorting and/or filtering is desire, that can also be specified here.
        // Note that sorting requires implementing SortablePropertyHandler.
        container = EventListContainerFactory.create(data, handler, UI.getCurrent());

        // Use the container wherever you normally would...
        table = new Table("People", container);

        Button manipItem = new Button("Manipulate");
        manipItem.addClickListener(new ClickListener() {

            private static final long serialVersionUID = 1L;

            @Override
            public void buttonClick(ClickEvent event) {
                container.getContainerProperty(3, "age").setValue(2);
                table.getContainerProperty(3, "age").setValue(10);
            }
        });
        this.addComponent(manipItem);

        Button addItem = new Button("Add");
        addItem.addClickListener(new ClickListener() {

            private static final long serialVersionUID = 1L;

            @Override
            public void buttonClick(ClickEvent event) {
                data.add(new Person("A", "B", ThreadLocalRandom.current().nextInt()));
            }
        });
        this.addComponent(addItem);
        this.addComponent(table);
    }

    private EventList<Person> makeEventList() {
        EventList<Person> data = new BasicEventList<>();
        data.add(new Person("A", "B", 5));
        data.add(new Person("C", "B", 2));
        data.add(new Person("D", "B", 4));
        data.add(new Person("E", "B", 3));
        return data;
    }

    private PropertyHandler<Person> makePropertyHandler() {
        PropertyHandler<Person> handler = new PropertyHandler<Person>() {
            @Override
            public Collection<?> getPropertyIds() {
                return Arrays.asList("firstName", "lastName", "age");
            }

            @Override
            public Class<?> getType(Object propertyId) {
                switch ((String) propertyId) {
                    case "firstName":
                    case "lastName":
                        return String.class;
                    case "age":
                        return Integer.class;

                    default:
                        throw new IllegalArgumentException();
                }
            }

            @Override
            public Object getValue(Person person, Object propertyId) {
                switch ((String) propertyId) {
                    case "firstName":
                        return person.getFirstName();
                    case "lastName":
                        return person.getLastName();
                    case "age":
                        return Integer.valueOf(person.getAge());

                    default:
                        throw new IllegalArgumentException();
                }
            }

            @Override
            public void setValue(Person object, Object propertyId, Object value) {
                switch ((String) propertyId) {
                    case "firstName":
                        object.firstName = (String) value;
                    case "lastName":
                        object.lastName = (String) value;
                    case "age":
                        object.age = (Integer) value;
                }
            }

            @Override
            public boolean isReadOnly() {
                return false;
            }
        };
        return handler;
    }

    public class Person {
        public String firstName;
        public String lastName;
        public int age;

        public Person(String firstName, String lastName, int age) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.age = age;
        }

        public String getFirstName() {
            return firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public int getAge() {
            return age;
        }
        // Getters here (exercise for the reader)
    }

I was wrong. The Table does update when I update a property from the container.
We have done so many hacks on Table to get it to work and now I’m working with Grid where I do have this problem, so I get confused…
( I had to write a small test program to convince myself that Table worked)

So, when I re-read your original post, it sounds like the problem is that your update some stored values through a property, and want some computed values to be updated.

To do that the property that wraps the computed values has to fire a valueChange event.

I can think of some ways around this:

  1. Manually do computedProperty.setValue(computedProperty.getValue())
  2. Create your own Property class that exposes fireValueChange and manually call that
  3. Create your own Property class that listens to other Properties and fires a valueChanged event when they fire a valueChanged event.

(1) requires a setter for the computed property, but the setter can be empty

Both 2&3 require you to also override Container.getItem to return your own Item class, where you can override Item.getItemProperty where you can return your own special ComputedProperty.

Thank You for Your reply!

I already managed to fire a event when a TextField Value is changed and there is also a notification-chain in my model that leads to updated computed values. These values are also correctly updated in the Container. The only thing I am asking myself is, if the refreshRowCache() call is really necessary as the Java-Doc says it is not for updating a property. But the ways I do in the above examples refreshRowCache() is called, so how can I change/update a property without getting it called? In the end it is just a performance question, as it seems to make the view quite lazy…

I thought the earlier developers of the software chose GlazedList for these reasons, but obviously that doesn’t help.

If everything is changed through properties then it should not be neccessary to call refreshRowCache.

We have a CellStyleGenerator that marks negative numbers with a “negative” class, to colour them red.
If we changed a value from positive to negative, through a property, the displayed value would be updated, but not the css class. To force that to update as well we would have to call refreshRowCache.

Another case is if you use Table.addGeneratedColumn.
I assume you have to call refreshRowCache to update these as well

I really just want to update the properties, no change in CellyStyle or whatever would need a repaint. Can You give me a small example how to change a value without getting refreshRowCache called? Above example with GlazedList Container doesn’t work. Same to this try:

@Override
    protected void initViewComponents() {
        this.setSizeFull();

        // Use the container wherever you normally would...
        table = new Table("People");

        table.addContainerProperty("firstName", String.class, "");
        table.addContainerProperty("lastName", String.class, "");
        table.addContainerProperty("age", Integer.class, 0);

        Button manipItem = new Button("Manipulate");
        manipItem.addClickListener(new ClickListener() {

            private static final long serialVersionUID = 1L;

            @Override
            public void buttonClick(ClickEvent event) {
                table.getContainerProperty(1, "age").setValue(5);
            }
        });
        this.addComponent(manipItem);

        Button addItem = new Button("Add");
        addItem.addClickListener(new ClickListener() {

            private static final long serialVersionUID = 1L;

            @Override
            public void buttonClick(ClickEvent event) {
                table.addItem(new Object[] { "A", "B", 10 }, table.getItemIds().size());
            }
        });
        this.addComponent(addItem);
        specialPerson = new Person("A", "B", 16);
        table.addItem(new Object[] { "C", "D", 12 }, table.getItemIds().size());
        table.addItem(new Object[] { specialPerson.firstName, specialPerson.lastName, specialPerson.age }, table
            .getItemIds().size());
        this.addComponent(table);
    }

When you use Table in this way, it has a copy of the data in its own internal Container.

When you do: table.addItem(new Object[] { specialPerson.firstName, specialPerson.lastName, specialPerson.age }, table .getItemIds().size()); you are adding a new item to this internal Container, and this item will have copies of the given values.
When the values in the internal Item are changed, they will never be written back to your specialPerson.

If you want that to happen, you must use something like a BeanItemContainer:

[code]
final BeanItemContainer personContainer = new BeanItemContainer<>(Person.class);
personContainer.addBean(specialPerson);
personContainer.addBean(new Person(“C”, “D”, 20));

    table.setContainerDataSource(personContainer);

    Button manipItem = new Button("Manipulate");
    manipItem.addClickListener(new ClickListener() {

        private static final long serialVersionUID = 1L;

        @Override
        public void buttonClick(ClickEvent event) {
            table.getContainerProperty(specialPerson, "age").setValue(5);
            Notification.show("Age in specialPerson is now " + specialPerson.age);
        }
    });
    layout.addComponent(manipItem);

[/code]Now the cells in the Table has Properties that will read/write values directly from/to the actual Person instance.
When we set the “age” property, specialPerson.age is updated and the Table cell is updated.

I have no experience with the GlazedLists Vaadin Container. In theory it should do something similar to what BeanItemContainer does, but perhaps it isn’t correctly implemented?

Thanks for Your reply. I couldn’t try Your suggestion until now, as I haven’t been in the office.

When I use Your suggested code, the .setValue calls the table.valueChange(Property.ValueChangeEvent event) method which calls refreshRowCache. So for me it still seems to be impossible to change a value in a Vaadin-Table without discarding and recreating the internal row cache.

By the way, changing specialPerson.age directly changes the stored value in the container, but the change isn’t displayed. So changes can only be displayed by refreshRowCache()?

Interesting fact:

when making the fields editable, a property change via the bounded property (e.g. specialPerson.setAge(x)) by the ButtonClick is visualized immediatly without having refreshRowCache() called.

What is the intention of redrawing the table when changing a bounded property which is only displayed, but not redrawing when changing a editable bounded property?

hy is the complete table redrawn when changing only one little value in a cell, but not when changing it in an editable cell?

Can anyone explaing this behaviour? Why can an editable cell change without refreshRowCache(), but a non-editable cell allways calls refreshRowCache() if its bound property changes?

Sorry, I’m the GlazedLists Container add-on author and just noticed this thread. You may be way past this by now, but for posterity I think I’ve given details in this forum thread as to how you can “kick” the model when your data objects change: https://vaadin.com/forum#!/thread/10939074

Short answer: you can use
set
on your list to signal that an element has changed or use ObservableElementList to signal automatically if your objects implement Observable.