Why removing components from form when attaching item

Hi,

I want to create my own layout and then add a data source to the form in my layout. But the form removes my components from my layout when I attach the data source. I want to let my text fields and so on in the place where I put them in my layout but when I set the data source, the text fields are created again and added to the end of my layout.

I found this method:


 public boolean removeItemProperty(Object id) {
        ownProperties.remove(id);

        final Field field = fields.get(id);

        if (field != null) {
            propertyIds.remove(id);
            fields.remove(id);
            layout.removeComponent(field);
            field.removeListener(fieldValueChangeListener);
            return true;
        }

        return false;
    }

I know, you have to remove the old listeners but I do not understand, why you remove the component from the layout. Maybe this is useful for generic layouts but for my self created layout this is deadly. Could you please add a method like



  protected boolean shouldRemoveComponentFromLayout() {
     return true;
  }

  public boolean removeItemProperty(Object id) {
        ownProperties.remove(id);

        final Field field = fields.get(id);

        if (field != null) {
            propertyIds.remove(id);
            fields.remove(id);

            // check, if component should be removed from layout first.
            if(shouldRemoveComponentFromLayout() {
               layout.removeComponent(field);
            }
            field.removeListener(fieldValueChangeListener);
            return true;
        }

        return false;
    }

This extra method would probably solve my problem. At the moment I cannot overwrite some method because I have no access to the private fields at all which are used in the method removeItemProperty.

With regards,
Udo

When you set the data source, the new content of the form can be of a completely different type than the previous component, and even if it isn’t, you could end up in a situation where the user would see a different form content depending on what the form has previously displayed. The form (and the default field factory) is designed not to reuse fields by default for these reasons.

Instead of fully constructing the layout in advance, you could use a field factory (which can return your pre-created components if you want) and override Form.attachField() to control their placement.

The resulting code is a little longer than what pre-construction would require, and there should be improvements coming to the use of forms in Vaadin 7.

Hi,

I changed the constructors and 2 methods of the class com.vaadin.ui.Form.

Here the changes:



    private boolean customLayoutUsed;

    public Form(boolean customLayoutUsed) {
        this(null,customLayoutUsed);
        setValidationVisible(false);
    }

    /**
     * Constructs a new form with given {@link Layout}.
     * 
     * @param formLayout the layout of the form.
     */
    public Form(Layout formLayout,boolean customLayoutUsed) {
        this(formLayout, DefaultFieldFactory.get(),customLayoutUsed);
    }

    /**
     * Constructs a new form with given {@link Layout} and
     * {@link FormFieldFactory}.
     * 
     * @param formLayout the layout of the form.
     * @param fieldFactory the FieldFactory of the form.
     */
    public Form(Layout formLayout, FormFieldFactory fieldFactory,boolean customLayoutUsed) {
        super();
        setLayout(formLayout);
        setFormFieldFactory(fieldFactory);
        setValidationVisible(false);
        setWidth(100, UNITS_PERCENTAGE);
        this.customLayoutUsed = customLayoutUsed;
    }

    public boolean isCustomLayoutUsed() {
        return this.customLayoutUsed;
    }

and the 2 changed methods:


    public boolean removeItemProperty(Object id) {
        ownProperties.remove(id);

        final Field field = fields.get(id);

        if (field != null) {
            propertyIds.remove(id);
            fields.remove(id);
            if (!isCustomLayoutUsed()) {
                layout.removeComponent(field);
            }
            field.removeListener(fieldValueChangeListener);
            return true;
        }

        return false;
    }


    public void setItemDataSource(Item newDataSource, Collection propertyIds) {

      ....

        // 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) {
                    f.setPropertyDataSource(property);
                    if (!isCustomLayoutUsed()) {
                        addField(id, f);
                    }
                }
            }
        }
  }

With that change it is possible to let the pre-rendered layout as it is and just add the data source items to the fields. No change of the layout occurs anymore if the flag customLayoutUsed is set to true. If it is false then the generic layouting is done.

Your answer, that I should use attachField does not match my needs because my layout is already filled with my fields. In the code I even do not know how the layout looks like (it is created form a layout description which is created with a WYSIWYG editor).