How to display on-screen totals (via Java)

Greetings.


Question: How can I show totals of two fields on the client (browser) as the user enters data ?

A development programmer for longer than some folk have been alive, I am new to java and especially to Vaadin.
I have been struggling for a few weeks to produce on-screen totals in an elegant fashion using java.
I am hoping that some bright spark will be able to set me straight in this regard.


The context is this:
A dynamically generated form: the layout, fields and content all being retrieved from the database.
Some of the fields (visible on screen) must be calculated dynamically.
The fields to use to calculate the total are also dynamically determined.

In my previous life, I would do this using “standard web development” methods (mainly PHP and Javascript - easy, 25 minutes :-), but as you are no doubt aware, generating dynamic javascript that actually fires under Vaadin is something of a mission, and then if you are lucky enough to get that working, you have to include setDebugId() all over the place to achieve it. Messy, very messy.

What can I use in the TextField ValueChangeListener of fields that form part of the calculation ?

[list]
I need to reference the value of the field changed plus the value of companion fields to make the total.
[]
The name of the TextField I could not generate dynamically, so I cannot store it sadly.
[
]
The name of the Property can be generated, so that provided a possible clue
[/list]The biggest problem is how to reference the underlying Property ( no getPropertyDataSource for TextField).
Even though I can infer the back-end Property name manually via HashMap, I am unable to use that to generate a reference the actual property.

However . . . I have managed to achieve java-only on-screen totals - but very badly.

  • I use the setDebugId to carry information needed
  • I consume fields from a list of pre-defined TextField variables !
  • This limits the number of calculations supported to the number of calculation fields defined.
  • There are many horrid hardcoded segments to support this methodology

It is an ugly, hardcoded workaround that should definitely be kept away from children and sensitive viewers :wink: :wink:
Here is semi-pseudocode describing the process:-


	/* The hardcoded defines for available calcs */
	ObjectProperty propCalc1 =  new ObjectProperty("", String.class);
	TextField tfCalc1_1 = new TextField();
	TextField tfCalc1_2 = new TextField();
	... etc ...
	
	/* Select the next available calc field */
	ObjectProperty theCalcProp = nextCalc();		// returns propCalc1
	theField.setPropertyDataSource(theCalcProp);
	
	... and then later ...
	
	subField = new TextField();
	subField2 = new TextField();
	if (theCalcField.equals(propCalc1)) {
		tfCalc1_1 = subField;  subField.setDebugId("1"); 
		tfCalc1_2 = subField2; subField2.setDebugId("1");
	}
	if (theCalcField.equals(propCalc2)) {
		tfCalc2_1 = subField;  subField.setDebugId("2");
		tfCalc2_2 = subField2; subField2.setDebugId("2");
	}
	... etc ...
	
	//== Add listener to each field to handle changes in the value
	subField.addListener(new Property.ValueChangeListener() {
		@Override
		public void valueChange( com.vaadin.data.Property.ValueChangeEvent event)
		{
			DoCalc(subField.getDebugId(););
		}
	})

	... and the DoCalc(String debugStr) update routine . . .
		TextField work1 = new TextField();
		TextField work2 = new TextField();
		ObjectProperty workCalc = new ObjectProperty("",String.class);
		String sWhich = debugStr;

                /* set references to data source property fields */
		if (sWhich.equals("1")) { work1 = tfCalc1_1; work2 = tfCalc1_2; workCalc = propCalc1; };
		if (sWhich.equals("2")) { work1 = tfCalc2_1; work2 = tfCalc2_2; workCalc = propCalc2; };
		... etc ...

		... do the math here ...

		workCalc.setValue(theSum);	
	

It is ugly and unprofessional so I am really interested in any ideas in this regard.


How have you implemented on-screen totals ?

Robin.

Hi,

For fields (such as a TextField), you want
#getPropertyDataSource

For a screen/UI such as this, I would suggest having a map of Name->Property, For added bonus points, you could also build a map of name->List dependencies. In fact, if you had a bi directional map of name->property (or a revere map of property->name), you could do something like the following - essentially build up a model of what needs to be updated when a “name” changes.



  protected CalculatorListener sharedCalculator = new CalculatorListener();
  protected Map<String, ObjectProperty<Integer>> nameToProperty = new HashMap<String, ObjectProperty<Integer>>();
  protected Map<Property, String> propertyToName = new HashMap<Property, String>();
  protected Map<String, String[]> propertiesToUpdate = new HashMap<String, String[]
>();

  public void addProperty(String name, ObjectProperty<Integer> property, String... propertyNamesToUpdate) {
    this.nameToProperty.put(name, property);
    this.propertyToName.put(property, name);
    this.propertiesToUpdate.put(name, propertyNamesToUpdate);
  }

  protected TextField createTextField(String name) {
    TextField textField = new TextField(nameToProperty.get(name));
    textField.addListener(sharedCalculator);
    return textField;
  }


  @Override
  public void init() {
    addProperty("subtotal1", new ObjectProperty<Integer>(Integer.ZERO));
    addProperty("subtotal2", new ObjectProperty<Integer>(Integer.ZERO));

    addProperty("item1", new ObjectProperty<Integer>(Integer.ZERO), "subtotal1", "subtotal2");

    TextField txtSubTotal1 = createTextField("subtotal1");
    TextField txtSubTotal2 = createTextField("subtotal2");
    TextField txtItem1 = createTextField("item1");

  }


  protected class CalculatorListener implements Property.ValueChangeListener {
    @Override
    public void valueChange(Property.ValueChangeEvent valueChangeEvent) {
      @SuppressWarnings("unchecked")
      ObjectProperty<Integer> changedProperty = (ObjectProperty<Integer>) valueChangeEvent.getProperty();
      // The name of the property that has changed
      String name = propertyToName.get(valueChangeEvent.getProperty());
      // The new value of the changed property
      Integer value = changedProperty.getValue();

      // Update all the dependent properties
      String[] propsToUpdate = propertiesToUpdate.get(name);
      for (String nameToUpdate : propsToUpdate) {
        ObjectProperty<Integer> propertyToUpdate = nameToProperty.get(nameToUpdate);
        
        //TODO : Update propertyToUpdate with Value here
      }
    }
  }

It’s a pointer in the direction I would take - it certainly doesn’t work as it stands!

HTH a little,

Cheers,

Charles.