When creating a Flow component based on a Polymer template, I want to be able to read model property values for the properties initialized on the client side. Currently Flow resets any matching client-side property values when initializing the template model.
Example:
<dom-module id="example-template">
<template>
<span>[[timeZone]]</span>
</template>
<!-- Polymer boilerplate to register the example-template element -->
<script>
class ExampleTemplate extends Polymer.Element {
static get is() {
return 'example-template'
}
static get properties() {
return {
timeZone: {
type: String,
value: () => new Intl.DateTimeFormat().resolvedOptions().timeZone,
observer: '_onTimeZoneChanged'
}
};
}
_onTimeZoneChanged(current, previous) {
console.log(`timeZone changed from '${previous}' to '${current}'`);
}
}
customElements.define(ExampleTemplate.is, ExampleTemplate);
</script>
</dom-module>
@Tag("example-template")
@HtmlImport("ExampleTemplate.html")
public class ExampleTemplate extends PolymerTemplate<ExampleModel> {
public interface ExampleModel extends TemplateModel {
}
public ExampleTemplate() {
}
}
On my machine this element renders Europe/Helsinki (as expected).
In the console there is only one change for the timeZone property:
timeZone changed from 'undefined' to 'Europe/Helsinki'
Adding a getter for the timeZone property into the template model causes the property to be reset to null even though it's never assigned on the server side:
@Tag("example-template")
@HtmlImport("ExampleTemplate.html")
public class ExampleTemplate extends PolymerTemplate<ExampleModel> {
public interface ExampleModel extends TemplateModel {
String getTimeZone();
}
public ExampleTemplate() {
}
}
That renders an empty string, and in the console there are two changes for the timeZone property:
timeZone changed from 'undefined' to 'Europe/Helsinki'
timeZone changed from 'Europe/Helsinki' to 'null'
In some cases, it's desirable to preserve an initial client side value and asynchronously send it to the server. In other cases, it's desirable to use the initial value from the server and use that to override the default from the client.
We can in general not assume that a server-side null value means that the property should be synchronized from the client.
Would we need to introduce an annotation for defining which option to use?
I don't think we need to do anything here at all in addition to what we have already.
Properties (plain element properties) _already_ have appropriate impl:
The problem described in this issue is: model properties doesn't work in this way.
It's because of the workaround we use to populate the model properties: we call the model and then all model properties gets nulls as default values.
That doesn't happen with plain element properties.
So we need to cancel this workaround and make model properties work in the same way as plain element properties. Then the algorithm is explained above.
And I already have an idea how to propagate model properties without setting then explicitly from the server side.
I suspend my work on this.
Here is a branch with the code implementing the idea: https://github.com/vaadin/flow/tree/2897-server-side-properties
Unfortunately there are a number of serious problems (also a number of failing tests because if those problems):
getMode() initializes immediately all model properties to their default values which are sent to the client side. So even if we don't call getModel() in the template CTOR by ourselves as internal impl the developer may call it to set some property e.g. As a result all unrelated props will be set to their default values and we still have an issue.StateNode as a value in its parent property. So we need the StateNode which should be a default value of the model in that case.StateNode : it has to be attached when it's handled on the client side. But it has to be in some feature of the element StateNode (so it needs to be attached). It cannot be called with JS execution being detached. It means that the code in the branch cannot work for the StateNode as a value (representing a bean) and we cannot do anything with subproperties here.So there can be mixed approach:
So it still requires getModel() call in the template CTOR but then simple properties should be removed from the ElementPropertyMap if they are not set explicitly.
And to be able to make it work we should not set initial values when the model is created. Otherwise every property is considered as explicitly set.
On the other hand not simple properties (bean type properties) should be initialized.
This ticket is extremely complicated even though it looks as very simple.
And the problem that is the heart of the issue: we need to propagate somehow model properties to the client side even though they are not set.
That's solved easily via getModel() call with the current implementation.
And it becomes a serious problem without getModel() call.
Actually I still think that this can be done excluding subproperties (at least for now).
I will keep this ticket assigned to myself.
I'm going to make some implementation when I have a time.