Let’s look at the two main use cases. We’ll look at the easiest one first – the convenience case. This involves being able to step into an object and manipulate that object using some of the functions that are available to it.
This first workflow that we’re looking at is very simple. I’m going to step through all the different pieces of it so that it’s very clear what it does. Each of these activities in the workflow is a “Create Value” activity. The first activity, “Get Map”, allows us to go and get the map.
Next, we’re going to get the “Layers Object”. There is an actual Get Layer activity in Geocortex Workflow, but this is an easier way to run this in the sandbox. So, I’m going to be manipulating the map’s layers collection.
The first thing I’m doing is simply stepping into the map object. We’re using the 4.x API. In Workflow we get the map. We can see in the “Input Editor” a rather odd “map.map.layers”. This is a layers object. The layers object in the 4.x API includes an item collection of layers. So, that is our first step.
In the second step we’ll go into the “Layers Collection”. Again, we’re just stepping in that layers object we made earlier. In the “Input Editor” we can see “layersObject.result.items”. The “items” is a collection of the of the layers.
For each layer in our layers collection, the arrow function is iterating over all the layers of that item collection. On the other side of the arrow, we can step into the actual object. It should say “layer.layerID == 2”. We know that the parcel layer ID is 2, so that will allow us to find that particular layer.
Again, there is an activity that can find you the layer no problem. You wouldn’t want to do this typically. But it’s a nice way of demonstrating the arrow notation and the find function.
A word of warning, the find function isn’t supported by all browsers. Older Internet Explorer browsers don’t support find. If that’s what you’re using, you can also use a filter function. So, you would just switch “find” to “filter”. This allows you to filter the layers collection into an array that only contains layers that have the layer ID equal to two, which would only be one layer. Instead of returning a layer object, you’re returning a collection of one layer object. To get at that layer you would need to add “” to the end of your expression to get the first one. The better function is find. Most browsers support it, almost all of them do. But it’s a word of warning in case you have users that are using an archaic browser that doesn’t support it.
So that’s one function that collections often have. There are other functions that are available based off the different objects in the workflow. As you’re walking through an object you can put a dot at the end of your expression. If there’s a function available, it will appear from the IntelliSense and give you options about what’s available. There are more functions available than that that are listed. It’s up to you to figure out what functions a particular object has available and whether they are supported. There’s some exploration possible if you’re inclined to use these functions.
So, I’ve broken the workflow down into four steps. Typically, you wouldn’t have this breakdown, but I wanted to make it clear how it works. What you would typically do is use a single expression. I’ve done that here in “Create Value” at the bottom of the workflow.
Let’s crack that open. You can see in the “Input Editor” we’re dotting into the map object. We have an expression that says “$map1.map.map.layers.items.find (layer => layer.layerID == 2)”. Layers will always have items. It could be empty, but it will always be there. And we can find it and that will return us our parcel layer.
So, it’s possible to take all of the broken-down functions in our workflow precluding “Layers Object” and break them down into one single expression.
Here we have “GetMap”. You can see we’ve got the outputs of the “map”, which is a “map”. And down here are our “layers”. In the other expression here we’re getting the “layersObject”, which was that “map.map.map” layer which returns our layers object. You can see here in “layers” are “items”.
From there we can then step into the next step, which is the layers collection which was where I was using the “layersObject” and the “layers.result.items” to get the layers. Here we have a collection of layers.
Here we have the “taxLayer”, which was from that find expression with the arrow notation. This returns us the actual result. This is the layer object.
Here is our “SingleExpression” where we compressed all the activities into one. We get the same result as the tax layer.
So, that’s the main use case. There are other instances, as I mentioned earlier, where you’ll want to step into a function to execute a particular operation. For example, manipulating a JSON object. There are two use cases for this. The first is if there isn’t an activity that supports it in a workflow, and you don’t want to go through the process of building a custom activity to do something as simple as JSON object manipulation. The second is performance, especially around for-each loops.
So, let’s take a look at that. Here we have a slightly more complex workflow. We’ve carried over some parts from the previous workflow. We’ve got the map and we’re using that “Single Expression” to get the tax layer. Then what I’m doing is using the tax layer to “Query Layer”. I’ll query the tax layer to get a feature set of all the features in the tax layer that will allow me to do so. So, I’m going to end up with 2000 parcels.
Next what I’ll do is create a collection, or an empty array, of active parcels. This is just a test. A confirmation of whether I want to go fast. I’m setting a timer. If I want to go fast, I’ll “Evaluate Expression”. If I want to go slow, I’ll do the “For Each”. At the very end of the workflow, I’m using “endTime” and then I’m jumping over to create the time and alert how much time it took.
Let’s jump right into the “For Each” loop here on the right. What I’m doing is manipulating some of the parcels. This doesn’t really mean anything, it’s just an exercise to update a particular feature. I’m checking to see if the status of the parcel is active. If it is, I’ll “Create Feature”. Then I’ll “Set Feature Attribute” to that new features “Attribute Name” so it’s the same as the feature that I’m iterating over. It’s kind of meaningless, but it’s just a way of exercising a for-each loop.
We’ll crack this open and the first thing you’ll notice is that I’ve got a function but it’s an anonymous function, I haven’t given it a name. The reason that I can do this is because I’ve set the scope by putting an open parenthesis and a close parenthesis around the expression. That allows me to define the scope of the function and have it run immediately. Basically, I’ve wrapped the function in a scope that allows me to use these extra parentheses right here to pass in some parameters.
I’ll break that down a bit so it’s clear. We’ve got our open parentheses and you define your function. “activeParcels” are the inputs to our function. This is the actual code inside of the function, and then we’ll close the function. We have the close parentheses and then another set of open and close parentheses around our parameters. Our parameters in this case are “$query1.features” and “$activeParcels.result”. “$query1.features” is our feature set returned from the query. “$activeParcels.result” is that empty array.
If you look at the loop what we’re doing is iterating over each “feature of features”. We’re checking to see if the state of “STATUS” is equal to “ACTIVE”. Then we’re creating a JSON object, which is a feature object. We’re setting the JSON objects “NAME”, setting the JSON objects “f.attribute” collections “NAME” to the “features.attributes[“NAME”]”. Then we set the “f.geometry” and add it to the “activeParcels”.This is pretty much the same thing that we did in the other loop.
The real takeaway is that you want to have an open parenthesis and close parenthesis containing your function. That sets the scope. Then you can pass in parameters to your function from your workflow using the dollar sign notation and the attributes parentheses. So, we’ve got open and close parentheses right here with the parameters, or inputs, of our function.
Let’s take a look at this. We’ll jump over to our sandbox. We’ll run this. A prompt comes up asking “Go Fast?”. We want to go slow. When we go slow, we’ll be running the for-each loop in the workflow and that will be seizing the for-each activity. So, we’ll click “Cancel”.
It tells us that it took “2.592 seconds” to iterate over 2000 records. That’s not terrible, but if you had 50,000 records, you’d be waiting a long time.
Let’s run this one more time and go fast. We’ll click “OK” to go fast. It only takes “0.003 seconds” this time so you can tell right away that it’s much faster to run a function than it is to do a for-each loop. This is a real savings. If we had 50,000 records instead of 2000 it would still be a bit slow, but nothing compared to the for-each loop. So, this is a really good use case for using a function.
Now a couple of handy things. You can’t jump into functions. You can see that we have “Evaluate” and it doesn’t really tell you anything in the console. So, if there’s a problem in your function and it doesn’t work, how are you going to be able to debug it? There’s a handy thing that you can do. Within your function you can add a line called “debugger;”. What this does causes the code to break on that particular line.
Then we can start walking through the code. Because we’re walking through the code, we’re able to jump into particular objects, take a look at them and then debug them. So, this debugger line is a lifesaver especially if you have a complex function that you need to debug.
A final caveat that you need to be aware of is that because we need to ensure that we are running safe code, it isn’t possible to create a new object in one of these expressions. So, for example, if I wanted to write “var x = new Feature ()”, that is not available. Because we’re running our code in a strict mode, this would be a security violation and you’re not allowed to do that. You’re only allowed to manipulate objects as they’re available, you can’t create new objects within an expression function. So, that’s just something to keep in mind.
Want to learn more about the capabilities of Geocortex Workflow? Click the button below for more information on what is possible with Geocortex Workflow.