If This, Then That: Conditional Logic and Document Generation
This is a deep dive into conditional logic with Document Generation APIs — this quick read is full of helpful tips and tricks to master this concept today.
Join the DZone community and get the full member experience.
Join For FreeWelcome to a deep dive into conditional logic in our Document Generation API. If you have not yet checked out this new tool, be sure to read my introduction to get a handle on the basics. Remember that you can test this service (and our Adobe PDF Tools API) for free for six months — there is no need to provide a credit card to sign up.
Hopefully, you’ve read the introduction and signed up, because now I’m going to take you into a deep dive on conditionals. I know, I know, you may be thinking... “Ray, that sounds too exciting. Can I safely read this article at work and maintain a proper level of decorum?” Don’t worry — I promise this will be both educational and (properly) fun. Let’s dive in!
First off, for all my demos below I’m going to be using the following JSON input:
{
"name":"Raymond Camden",
"email":"raycamde@adobe.com",
"address":"1313 Mockingbird Lane, Lafayette, LA",
"cell":"337-555-5555",
"cats":[
{"name":"Luna", "gender": "female", "breed": "something", "weight": 4},
{"name":"Pig", "gender": "female", "breed": "something else", "weight": 8},
{"name":"Cracker", "gender": "male", "breed": "large", "weight": 10}
]
}
With this input data, we will use a Microsoft Word document. All the files are available on my GitHub repository. With that out of the way, let’s get started!
Basic (and Not So Basic) Examples
Addressing Blank Fields
Let’s begin with the simplest example. Given our data before, we’d like to conditionally display both the address and cell fields for a person if they exist.
As I described in my introduction, we can use the Document Generation Add-in in Microsoft Word to generate tags based on our data. I did that with the JSON above. To do basic conditionals, you can select the “Advanced” tab and expand “Conditional content”. I’ve selected a paragraph of text, and then used the Add-In to pick a value to check:
After clicking Insert, you can see the following now in the Word document:
{% conditional-section address %}
My address is {{address}}.
{% end-section %}
At this point, if you feel comfortable, you can probably insert the condition for a cell phone by hand. In this example, we will add the sentence “My cell phone number is {{cell}}”. It should look something like this:
{% conditional-section cell %}
My cell phone number is {{cell}}.
{% end-section %}
So far so good. You will notice in the conditional-section above that it says {% conditional-section cell %}. Similar to if statements in languages like JavaScript, this is simply looking for the existence of a value. What if our JSON had this:
"cell":""
In this case, you end up with this in the generated document: “My cell phone number is .” This is not a failure as there was a value for the cell field, it was just an empty value. You then may try something like so:
{% conditional-section expr(cell != "\"\"") %}
Technically, it’s assuming you meant the literal value of two quote marks. In JSON that would be like this:
"cell":"\"\""
If you really want to match an empty string, you would modify the condition by hand:
{% conditional-section expr(cell != "") %}
This will work as expected, but you have to understand your data. If you are providing data from an API, what does it do for a customer or data record where a cell doesn’t exist? Does it leave it out? If so, the initial version we used is best. If it includes it but leaves it blank, then the last example is what you would need.
Adobe Document Generation API can handle a lot of things you throw at it, but you will also need to have a good understanding of what you’re going to be passing in. If your data is coming from another API, you’ll be responsible for checking their documentation to see how it acts.
ELSE Conditions
Let’s make this a bit more complex because complexity is fun, right? What if you want to support an “ELSE” condition? For example, if a cell phone number doesn’t exist, output some text saying as such.
The template language used in Document Generation doesn’t support an ELSE condition. You can instead use two conditions — one for the positive and one for the negative. For example:
{% conditional-section expr(cell != "") %}
My cell phone number is {{cell}}.
{% end-section %}
{% conditional-section expr(cell = "") %}
No cell on file (please provide one as soon as possible).
{% end-section %}
A slight variation of the theme, and perhaps not technically a “conditional”, is the use of defaults and optional values. A default value can be provided for variables by using :default-val("...")
syntax. So for example:
My cell is {{cell:default-val("unknown")}}.
However, keep in mind this follows the same logic as above. If cell
exists and is an empty string, the default value will not be used.
Optional values are done using the optional
keyword in a tag, like so: {{cell:optional(true)}}
. In this case, if the cell doesn't exist, nothing is outputted at all.
Conditional Looping
While this article is focused on conditions, let’s quickly look at how looping works. Given that our input has an array of cats, we can loop over them like so:
{% repeating-section cats %}
• {{ name}}
{% end-section %}
You can write this out by hand or use the Add-In to generate the code for you. Notice how within the context of the loop, {{name}}
refers to a property of an individual cat, not the JSON object at the top level. This works as you may expect:
- Luna
- Pig
- Cracker
This is cool, but what about combining loops with conditionals? This can be done by adding a condition to the array name:
{% repeating-section cats[weight > 5] %}
In the example above, I’m asking for a repeating section over the items in the array where the weight property is greater than 5. This returns a shorter list:
- Pig
- Cracker
This leads to an interesting issue. Imagine you want to output a list of cats over a certain weight, but before that list, you want to explain why. So your template may look something like this:
Overweight Cats: Here is a list of overweight cats: {% repeating-section cats[weight > 5] %} * {{ name }} {% end-section %}
What happens if the array is empty? You end up with text referring to a list of cats that don’t exist. There’s a fix for this as well. JSONata is a wealth of information for folks using Document Generation API and it’s there I discovered the $count function. It returns the number of items in an array. To wrap the text above in a condition to ensure it only shows up when at least one cat meets the criteria, use:
{% conditional-section expr($count(cats[weight > 5]) > 0) %} Overweight Cats: Here is a list of overweight cats: {% repeating-section cats[weight > 5] %} * {{ name }} {% end-section %} {% end-section %}
You do have a bit of repetition in there so be careful if you ever change the definition of your criteria. Also remember that ultimately, you have control over the data you send to your document. You could, for example, set a variable in your JSON that’s a boolean value representing the fact that overweight cats exist. Then your Word document logic gets a bit simpler:
{% conditional-section overweightCatsExist %} Overweight Cats: Here is a list of overweight cats: {% repeating-section cats[weight > 5] %} * {{ name }} {% end-section %} {% end-section %}
In the example above, we assume a true or false value for overweightCatsExists
. For developers who have used template languages in the past, you may have dealt with this before. Some languages prohibit complex logic in the document and you have to use variable "stand-ins" as shown above.
Try It Yourself!
Want to give it a shot? You can sign up now for a free six-month trial. The scripts I used for testing what I demonstrated today may be found on my GitHub repo. I’ve also built a generic Node.js script that makes it easier to test. Clone my repository, ensure your credentials are copied over, and you can then test document generation by calling the script like so: ./generic.js input.docx output.pdf mydata.json
.
Published at DZone with permission of Raymond Camden, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments