Closed avernet closed 6 years ago
There are two distinct aspects to controls within repeats:
fr:grid
does the updates, in particular.)So say you have 2 iterations, and you insert a row before the first iteration. The server sees:
A C → new control with new value
B A → xxforms-iteration-moved
B → xxforms-iteration-moved
While the client must see:
1 1 → existing control with updated value
2 2 → existing control with updated value (doesn't work right now)
3 → new control with new value
We can think of two solutions, as suggested:
xformsUpdateValue
, etc.
iterationMoved()
function in the JavaScript lifecycle API.Some of this is discussed in "Smarter repeat updates between client/server" (#1274) from 5 years ago.
Open question, based on #1274: if we choose the second solution, do we need to "assign new unique ids to iterations, so that the client can keep references from id to JavaScript structure"?
It is unclear whether #1274 rightly calls for different ids. The client maps instances of XBLCompanion
to the component's container DOM element using the data
API with key xforms-xbl-object
. If, say, the client moves an element in the DOM in response to a server event, the element will be preserved and the associated data as well. We could add a lifecycle call to indicate that the iteration/position has changed, but I am not sure we need a new id scheme.
The xformsUpdateValue
on the companion is called on the client when receiving a value from the server. So if we want to use this, we need the server to communicate that.
The server does send an empty <value/>, but then it also calls a script
xf_0cfac97c799fcfcae00c2263a166faec9b60753e`.
<xxf:event-response xmlns:xxf="http://orbeon.org/oxf/xml/xforms">
<xxf:action>
<xxf:control-values>
<xxf:copy-repeat-template id="fr-view-component≡my-section-section≡grid-2-grid≡grid-2-grid-repeat" parent-indexes=""
start-suffix="2" end-suffix="2"/>
<xxf:control class="+can-remove +can-move-down" id="fr-view-component≡my-section-section≡grid-2-grid≡xf-742⊙1"/>
<xxf:control id="fr-view-component≡my-section-section≡grid-2-grid≡my-date-control⊙1">
<xxf:value/>
</xxf:control>
<xxf:control class="fr-grid-repeat-iteration can-remove can-move-down can-insert-above can-insert-below fr-grid-body"
id="fr-view-component≡my-section-section≡grid-2-grid≡xf-742⊙2"/>
<xxf:control id="fr-view-component≡my-section-section≡grid-2-grid≡xf-762⊙2" class="fr-repeat-column-left"/>
<xxf:attribute for="fr-view-component≡my-section-section≡grid-2-grid≡xf-763⊙2" name="aria-label">Menu</xxf:attribute>
<xxf:control id="fr-view-component≡my-section-section≡grid-2-grid≡xf-765⊙2" class="fr-grid-td"/>
<xxf:control id="fr-view-component≡my-section-section≡grid-2-grid≡my-date-control⊙2" class="fr-grid-1-1" label="Date"
init="true">
<xxf:value>2018-10-02</xxf:value>
</xxf:control>
<xxf:attribute for="fr-view-component≡my-section-section≡grid-2-grid≡my-date-control≡xf-778⊙2" name="placeholder">MM/DD/YYYY
</xxf:attribute>
<xxf:control id="fr-view-component≡my-section-section≡grid-2-grid≡xf-767⊙2" class="fr-grid-td"/>
</xxf:control-values>
<xxf:repeat-indexes>
<xxf:repeat-index id="xf-276" new-index="0"/>
</xxf:repeat-indexes>
<xxf:script name="xf_0cfac97c799fcfcae00c2263a166faec9b60753e"
target-id="fr-view-component≡my-section-section≡grid-2-grid≡my-date-control⊙1"
observer-id="fr-view-component≡my-section-section≡grid-2-grid≡my-date-control⊙1">
<xxf:param>[M]/[D]/[Y]</xxf:param>
</xxf:script>
</xxf:action>
</xxf:event-response>
That's in response to this:
<xbl:handler event="xxforms-visible">
<xf:action type="javascript">
<xf:param name="format" value="xxf:property('oxf.xforms.format.input.date')"/>
<xf:body>ORBEON.xforms.XBL.instanceForControl(this).setFormat(format)</xf:body>
</xf:action>
</xbl:handler>
The control on the first iteration is newly-visible, runs that script, which sets the format. The implementation of setFormat
calls the date picker to set the current date again. On the client, the date picker is that associated with the previously-selected date on the previous first iteration.
def setFormat(format: String): Unit = {
// On iOS, ignore the format as the native widget uses its own format
if (! iOS) {
val date = datePicker.getDate
datePicker.options.format = format
.replaceAllLiterally("[D]", "d")
.replaceAllLiterally("[M]", "m")
.replaceAllLiterally("[Y]", "yyyy")
datePicker.setDate(date)
}
}
Verified that the reason it is not working is that when we get a blank value, we try to parse it as an ISO date, and we fail, and the picker stays the same. Instead, we must handle the case of a blank value separately and call datePicker.clearDates()
.
In addition, the format doesn't get set when iterations are moved, and so we must respond to xxforms-iteration-moved
.
Entered #3765 for a declarative way of propagating XBL parameters to the client.
To reproduce:
bootstrap-datepicker
.The newly added row has been initialized correctly, however the first row incorrectly still shows the same date, while it should be empty. This component implements methods from the JavaScript livecycle instead of reacting to XForms events. On the client:
Doing things this way is OK, but then the controls on the first row should be notified that their value has changed, e.g. was reset in the above example. And maybe this should be consistently done for methods of the JavaScript lifecycle and for XForms events, even if the latter might be more tricky, depending on when the events are dispatched.