In this weekly series, Martin describes a question that was raised by a Fonto developer, how it was resolved and why Fonto behaved like that in the first place. This week, a developer reached out! They have a modal that manages a prolog: it can either insert a new prolog, or it can edit one. How can they prevent a modal from opening that cannot do anything?!
A support ticket came in:
I have an operation that runs a custom modal. The modal will edit a specific element if it already exists and if it doesn’t, it will insert it then let you edit it.
The operation is on a menu button and I’d like to disable it when then context does not allow the element.. which seems like it should be simple, but I can’t figure it out.
So my question is, how in the operation getStateSteps can I check if an element exists OR if it’s already there. If I put a horizontal-insert or merge-and-insert in getStateSteps, that will check if it’s valid to insert, but then the button is disabled if the element already exists.
The operation just runs the modal. Then the modal internally runs multiple operations without closing.
Checking for missing prolog is easy by doing an insert in getStateStep, but what is the best way to check if the prolog element already exists?
Suggestions please! 🙂
A partner
The trick to this modal is that it can do multiple things, depending on what the author chooses and how the document looks. This can be done with alternatives
and getStateSteps
. We already discussed getStateSteps
in another why and how post: Why is my operation on a locked document enabled?!
In short, by default, the operations pipeline runs the steps
to determine whether an operation is enabled or not. The getStateSteps
steps can be used to override that. Instead of running the steps
, just run the getStateSteps
. This can be very useful for operations that are technically always allowed, but not actually usable.
The Operations documentation talks about alternatives. These can be used to express ‘forking’ in operations: first try one operation, otherwise choose the next one. Common use-cases are operations that should either try to wrap the selection in an element, or inserting it if there’s no selection. Check out the dita-example-editor: there it’s used to implement the :insert-note operation.
The flow for the operation can work a little bit like this:
-
Make the
getStateSteps
point to a single other operation, likeprolog-modal-getstate-operation
-
This new operation works with alternatives. Instead of a steps array, we have an alternatives array. First, we have an operation that inserts the
prolog
. Second, we have an operation that edits aprolog
: inserting a placeholder element for example. -
When getting the state of the operation, Fonto will go through the alternatives, checking whether one of them is enabled. If both are disabled, the whole operation is disabled.
-
To make this even better, we can use all the operations as alternatives to the
getStateSteps
. Basically expressing the state of the modal as ‘as one of my buttons will work, make the whole modal work’.
This will look a bit like this:
{ "open-prolog-modal": { "description": "Edit the metadata", "steps": { "type": "modal/prolog-modal" }, "getStateSteps": { "type": "operation/prolog-modal-getstatesteps" } }, "prolog-modal-getstatesteps": { "alternatives": [ "operation/set-attribute-on-prolog-author", "operation/insert-new-prolog" ] }, "set-attribute-on-prolog-author": { "steps": { "type": "operation/execute-update-script", "data": { "contextNodeId": "x__fonto:selection-common-ancestor/ancestor::topic/prolog/metadata/author[1]" , "expression": "if (not($data?contextNode)) then () else replace value of node $data?contextNode with 'Jane Doe'" } } }, "insert-new-prolog": { "steps": { "type": "operation/horizontal-insert", "data": { "contextNodeId": "x__fonto:selection-common-ancestor/ancestor::topic" , "childNodeContents": ["prolog", ["metadata", ["author", "Jane Doe"]]] } } } }
We used the execute-update-script operation on one side and the horizontal-insert operation on the other side. The execute-update-script operation is a very generic operation that can do anything that XQuery Update Facility allows, but it has difficulty when a certain element for instance can occur only on a very specific place (like a Dita prolog element). The smarter horizontal-insert can handle that: insert the prolog after the shortdesc, or abstract, if it is there. Just insert it wherever it fits.
This approach worked, the partner continued with the next challenges.
I hope this explained how Fonto works and why it works like that. During the years we built quite the product, and we are aware some parts work in unexpected ways for those who have not been with it from the start. If you have any points of Fonto you would like some focus on, we are always ready and willing to share! Reach out on Twitter to Martin Middel or file a support issue!
Developer advocate / Evangelist. Has been with Fonto since it all began in 2013. He’s currently designing the next steps in Fonto Developer APIs with the input of our valuable partners.
In his spare time, Martin is an avid home brewer.
Receive updates on new Fonto Why & How blog posts by email