Flow: Exception when trying to update an int value from the client to the model in Java

Created on 17 May 2018  路  11Comments  路  Source: vaadin/flow

If a property is defined as Number in client template (.html) and it tries to update its conterpart field in the Java Model that is also declared as a supported type, as an int, I get this exception:

Caused by: java.lang.IllegalArgumentException: The stored model value '1' type 'java.lang.String' cannot be used as a type for a model property with type 'int'
    at com.vaadin.flow.templatemodel.BasicModelType.modelToApplication(BasicModelType.java:64)

Source code used to reproduce the error:

<dom-module id="x-inter">
    <template>
        <input type="radio" name="toggle" id="id1" value= 1  on-change="_getSelected">One
        <input type="radio" name="toggle" id="id2" value= 2  on-change="_getSelected">Two
    </template>
    <script>
        class XInter extends Polymer.Element{
            static get is(){
                return "x-inter";
            }
            static get properties(){
                return {
                    selectedIndex: {
                        type: Number,
                        reflectToAttribute: true,
                        notify: true
                    }
                };  
            }
            _getSelected(){
                if(this.shadowRoot.querySelector('input[name=toggle]:checked')){
                    this.selectedIndex = this.shadowRoot.querySelector('input[name=toggle]:checked').value;
                }
                else
                    this.selectedIndex = -1;
                this.dispatchEvent(new CustomEvent('change', {}));
            }
        }
        customElements.define(XInter.is, XInter);
    </script>
</dom-module>
<dom-module id="x-element">
    <template>
        <x-inter selected-index= {{selectedValue}} on-change="_valueChanged"></x-inter>
    </template>
    <script>
        class XElement extends Polymer.Element{
            static get is(){
                return "x-element";
            }
            static get properties(){
                return {
                    selectedValue:{
                        type: Number,
                        notify: true
                    }   
                };
            }
        }
        customElements.define(XElement.is, XElement);
    </script>
</dom-module>
@Tag("x-element")
@HtmlImport("x-element.html")
public class XElement extends PolymerTemplate<Model> {

  public XElement() {
    this.getElement().synchronizeProperty("selectedValue", "_valueChanged");
    this.getElement().addPropertyChangeListener("selectedValue", e -> {
      System.out.println(getModel().getSelectedValue());
    });
  }

  public interface Model extends TemplateModel {
    int getSelectedValue();
    void setSelectedValue(int value);
  }
}
bug template

All 11 comments

@tdq Nice find. Should be enough to add a + in front of the expression to cast it to a number.

this.selectedIndex = this.shadowRoot.querySelector('input[name=toggle]:checked').value;
}
here you need to cast to int, for example use parseInt function, and

int getSelectedValue();
void setSelectedValue(int value);

here you should use double because js Number will be converted to java Double.

JavaScript doesn't make any distinction for integer numbers, so parseInt should not be necessary.

It shouldn't be necessary to change anything on the server either, Flow will simply take care of truncating any JS number received from the client to an int or double as appropriate depending on the context.

Because in this tag
<input type="radio" name="toggle" id="id1" value= 1 on-change="_getSelected">
the value attribute is set to a number and not string I presumed that it will also be a number what is returned from
this.shadowRoot.querySelector('input[name=toggle]:checked').value;

And I've tried to check the value of selectedIndex after being set to
this.shadowRoot.querySelector('input[name=toggle]:checked').value;
and it is a number.

well, anyway model waiting for int value, but this.shadowRoot.querySelector('input[name=toggle]:checked').value changes property type to String, so we need to cast it.

Yes, the string needs to be changed into a number, but there's no need to change it into any specific type of number.

so just retested and after calling this.selectedIndex = this.shadowRoot.querySelector('input[name=toggle]:checked').value;

isNaN(this.selectedIndex) tells me it is a number

So after parsing it to number Java complains of not being able to cast from Double to int type.

The stange thing is that parsing or not parsing the input value isNaN(this.selectedIndex) gives me a false true both cases.

Anyway of the parse to Number fix the String to int converstion problem how to fix the Double to int conversion problem now?

but again model requested int

int getSelectedValue();
void setSelectedValue(int value);

and flow will try to convert Number to double

isNaN is not guarantee that value is number if it returns false. It checks that value can be converted to number or nor, so isNaN("1") is false.

Seems like I was mistaken. Flow does in fact not automatically handle conversion from JS number to int in this case, so this is something that we should fix.

Until it is fixed, you can work around the problem using the suggested workaround of changing the model type to double instead of int.

Was this page helpful?
0 / 5 - 0 ratings