Often there will be situations where calculations in one model are dependent on data in another. For example, let's say we have a model that contains a list of actions that our user can perform:
<xf:model id="mdl-actions">
<xf:instance id="inst-actions">
<actions xmlns="">
<action id="add">Add</action>
<action id="delete">Delete</action>
</actions>
</xf:instance>
</xf:model>
We could now use this data to show a list of triggers to the user:
<xf:repeat nodeset="action">
<xf:trigger>
<xf:label ref="." />
</xf:trigger>
</xf:repeat>
Let's extend this so that each action contains a value that indicates whether the user can perform that action or not, and we set the relevance of the action based on that value:
<xf:model id="mdl-actions">
<xf:instance id="inst-actions">
<actions xmlns="">
<action id="add" allowed="1">Add</action>
<action id="delete" allowed="1">Delete</action>
</actions>
</xf:instance>
<xf:bind nodeset="action" relevant="boolean-from-string(@allowed)" />
</xf:model>
Now all we need to do to show or hide the trigger is bind it the action node (so that it is affected by the node's relevance):
<xf:repeat nodeset="action">
<xf:trigger ref=".">
<xf:label ref="." />
</xf:trigger>
</xf:repeat>
and then use setvalue to control whether a user can perform the action or not. For example, if the last item in a list is deleted, we might indicate that the 'delete action' is not available:
<xf:setvalue ref="action[@id='delete']/@allowed">0</xf:setvalue>
This is a very useful construct, but let's extend it now so that other data plays a role in determining what actions a user can perform. Let's say we have another model that provides some information about a user that has logged in to some system. This information would be provided dynamically, but to make it easier to follow, we've shown it here in an instance:
<xf:model id="mdl-login">
<xf:instance id="inst-login">
<user xmlns="">
<name>John Doe</name>
<role>admin</role>
</user>
</xf:instance>
</xf:model>
Now, let's add a bind statement to our previous instance that allows administrators to use delete:
<xf:bind nodeset="action[@id='delete']/@allowed" relevant="globalInstance('inst-login')/role[. = 'admin']" />
<xf:bind nodeset="action" relevant="boolean-from-string(@allowed)" />
</xf:model>
NOTE: We've used the formsPlayer globalInstance function, which allows references to be made to instances in different models. It's a fairly straightforward function, and no dependencies are created.
This should have the effect of setting the value of @allowed on the 'delete action' element based on whether our form user is an administrator or not. However, the key question here is when exactly is this calculation performed?
Recall from the recalculation phase that only those items that are in the change list will cause dependents to be recalculated. In other words, there is no 'recalculate all', except after a rebuild. In our example above, changes to any of the allowed attributes will cause a recalculation of the relevance of the corresponding action element, but changes to the value of globalInstance('inst-login')/role will not.
This obvious solution is to perform a rebuild on the 'actions model', whenever a rebuild is performed on the 'login model', for example, after the submission in the login model has completed:
<xf:model id="mdl-login">
<xf:instance id="inst-login">
<dummy xmlns="" />
</xf:instance>
<xf:submission action="..." method="..."
replace="instance" instance="inst-login"
>
<xf:rebuild ev:event="xforms-submit-done" model="mdl-actions" />
</xf:submission>
</xf:model>
However, there is an easier way, which is to put the relevance rule we added to the action model, into the login model; this means that the calculation will be performed as part of the normal course of events at the right time, whenever anything changes in the login model. Our finished mark-up is as follows:
<xf:model id="mdl-login">
<xf:instance id="inst-login">
<dummy xmlns="" />
</xf:instance>
<xf:submission action="..." method="..."
replace="instance" instance="inst-login"
/>
<xf:bind nodeset="globalInstance('inst-actions')/action[@id='delete']/@allowed" relevant="instance('inst-login')/role[. = 'admin']" />
</xf:model>
<xf:model id="mdl-actions">
<xf:instance id="inst-actions">
<actions xmlns="">
<action id="add" allowed="1">Add</action>
<action id="delete" allowed="1">Delete</action>
</actions>
</xf:instance>
<xf:bind nodeset="action" relevant="boolean-from-string(@allowed)" />
</xf:model>
What's interesting about this approach, is that the 'actions model' remains encapsulated; it deals only with whether an action can be performed or not, but has no 'knowledge' of the conditions that determine that. And from a programmer's perspective we're using bind literally to mean 'please add this calculation to the master dependency directed graph for this model'.


Recent comments
2 days 4 hours ago
7 weeks 2 days ago
7 weeks 2 days ago
10 weeks 6 days ago
12 weeks 2 days ago
12 weeks 3 days ago
12 weeks 3 days ago
12 weeks 5 days ago
12 weeks 5 days ago
12 weeks 5 days ago