A question came past the other day about how to preserve the previous value of a form control before the one currently entered by the user. Immediately, the most famous story from Italian playwright Luigi Pirandello came to mind. Like six characters in search of an author, here we have a few XForms techniques in search of a blogger, and those techniques are getting their 5 minutes of fame now because the topic I intended to present has fallen victim to a limitation of the blogging system.
I'm sure there are other ways to do this, and comments are of course welcome, but here is what came to mind for me.
First of all, the originator of the question was attempting to use a special XFDL form control that can contain its own data, rather than binding the XFDL form control to the XForms model. This was being done because they did not believe the previous values could be captured in a way that uses XForms. However, having another control that can contain its own data is really no different than having a control that binds to a separate piece of data in the data model, so if it can be done with the special XFDL control, then it can be done with XForms too. Moreover, I would assert that it is better to solve this problem using XForms all the way because then you will have a better chance of creating something that even works when the controls in question are inside a
repeat or when you need to save the form and reload with the previous values intact.
To be honest, once you buy into the fact that storing the previous value implies storing extra data, then you don't really need extra form controls to show the extra data to the user. You may need to show the previous values to the user, but it's trivial because you can just bind a control to the data node that stores the previous data.
So, now you get to learn about my Einsteinian philosophy for tackling programming problems. Remember the formula E=mc2? Well, mass is that which takes space, which is like memory, so mass is like variables. Energy is that which is expended over time, just like commands. So any time you're having difficulty figuring out what code to write to solve a problem, it's probably because you don't have enough variables. Put another way, often you can add just one variable and save yourself lots of code. This is what seems to be happening with the problem of preserving the previous value of any data node. So, suppose you store not just the previous value of a data node but also a copy of the current value. This problem gets a lot easier to solve. So let's take the following instance:
<xf:instance> <data> <node current="" previous="">VALUE</node> </data> </xf:instance>
So, the node has an initial value of "VALUE", and we want to be able to change the node content to, say, "NEW VALUE" while also preserving "VALUE" in the attribute named previous. So, easy stuff first is that we need to give the end-user the ability to change the node from "VALUE" to "NEW VALUE", and heck we might as well show the previous value to the user:
<xf:input ref="node"> <xf:label>Type a NEW VALUE in here</xf:label> </xf:input> <xf:output ref="node/@previous"> <xf:label>The old VALUE is here</xf:label> </xf:output>
Now the fun stuff. On form initialization, you would want to store the current value of the node because it will feed the previous value once the user actually makes some changes. You do this by capturing one of the XForms model initialization events, such as
xforms-model-construct-done. I use the latter one for making data changes, which you can see below in the
<xf:model> <xf:instance> <data> <node current="" previous="">VALUE</node> </data> </xf:instance> <xf:setvalue ev:event="xforms-model-construct-done" ref="node/@current" value=".."/> </xf:model>
Last but not least, we now need to respond to changes the user makes by updating current and previous. This can be done by modifying the
xf:input to respond to value changes to its bound node:
<xf:input ref="node"> <xf:label>Type a NEW VALUE in here</xf:label> <xf:action ev:event="xforms-value-changed"> <xf:setvalue ref="@previous" value="../@current"/> <xf:setvalue ref="@current" value=".."/> </xf:action> </xf:input>
Every time the end-user enters new input, the change is sent to the data model and placed in the bound node. The XForms model then ensures that all calculations are run and then dispatches the event
xforms-value-changed to all form controls that are bound to nodes that changed, including the control that made the change. We are hooking that event and then copying the current value to the previous value, then copying the new value in the node into the current value after that.
That's really the end of the story, so I'm going for dinner. Hmm. Italian sounds good!