Fetching an existing merged JSON Variable

Hi there,

The documentation here Working with Json Data | docs.camunda.org
describes how to fetch an existing JSON variable in case it’s created via camForm function.
Does anyone know please how to fetch a merged JSON variable which is set inside a script of an Execution Listener and includes the values from multiple instances ?
What I’ve achieved here is to see the values of this merged JSON variable in Cockpit but not in the next taskform of my process in Tasklist.

For anyone interested, I attach here the bpmn file with my process.

Thank you in advance,
Steve

orders.bpmn (8.6 KB)

Hi @steftriant,

In general, you can use camForm.variableManager.fetchVariable() to fetch any process variable, no matter where it was set. See also: https://docs.camunda.org/manual/7.10/reference/embedded-forms/javascript/lifecycle/

If this does not work for you, please share your form files with us so we can have a look at them.

KR
Martin

1 Like

Hi @martin.stamm,

Based on the paragraph about Fetching Additional Variables here Participating in the Form Lifecycle | docs.camunda.org, I tried to fetch my merged JSON variable (in which, all the submitted values are appended from the previous Multi-Instance user task), but without success :confused:

My relative script (as I can’t upload here my html files) for the task (“Receive the Selected Products”) which must receive the submitted values from the Multi-Instance task (“Select Products”) is the following:

<script cam-script type="text/form-script">
				var variableManager = camForm.variableManager;

				camForm.on('form-loaded', function() { 														// No any variable has been loaded from the server.
					variableManager.fetchVariable('aggregatedList');										// We declare the merged JSON variable 'aggregatedList' so as to fetch its values.
				});

				camForm.on('variables-fetched', function() {												// The merged JSON variable 'aggregatedList' has been fetched from the server.
					var variableValue = variableManager.variableValue('aggregatedList');
				});
			</script>

I’m not sure if I must change sth in my relative script of the Multi-Instance task (“Select products”) in which, the submitted values of each user are appended in the merged JSON variable (“aggregatedList”).
Here is my respective script:

<script cam-script type="text/form-script">
				camForm.on('form-loaded', function() {																																								// The variable 'product' has not been loaded ("fetched") from the server yet.
					camForm.variableManager.fetchVariable('product');																																				// We tell the "form SDK" to fetch the 'json' variable named 'product'.
				});
				camForm.on('variables-fetched', function() {																																						// The value of variable 'product' has been fetched (loaded) from the server and 
					$scope.product = camForm.variableManager.variableValue('product');																																// is bound to the current AngularJS $scope of the form.										
				});
				$scope.changeQuantity = function(x, index, chkselct) {																																				// "changeQuantity" function gets called at every change in the value of ("checkbox" or "quantity") input fields. 
					console.log('x: ' +x.Quantity+ ', chkselct: ' +chkselct+ ', index: '+ index);																													// We display (x, chkselct, index) values in the debugger window. 
					if (!chkselct) {																																												// If product's "checkbox" is not checked, then
						x.Quantity=undefined;																																										// product's "quantity" input field has not a value.
					}
					$scope.product[index] = x;																																										// Using "index" from the Array, we find the product, whose ("checkbox" or "quantity" input fields) value has changed.  
					$scope.total = total();																																											// "total" is bound to the current AngularJS $scope of the form as a function which gets called.
				}
				function total() {																																													// When "total ()" function gets called,
					var total = 0;																																													// variable "total" is initialized.
					angular.forEach($scope.product, function(x) {																																					// Based on the "index" from the Array, "forEach" function reads each product whose ("checkbox" or "quantity" input fields) value has changed.
						if (x.Quantity) { 																																											// If product's "quantity" input field has a value (so, product's "checkbox" is checked anyway), then
							total += x.Price * x.Quantity;																																							// we calculate "total" (by multiplying "product's price" by "product's quantity" and adding the last "total").												
						}
					})
					console.log('Total: ' + total);																																									// We display "total" value in the debugger window.
					return total;																																													// "return" statement stops the execution of "total ()" function and returns a value from that function.
				}
				camForm.on('submit', function(evt) {																																								// Before "submit" request is sent to the server,
					var selectedProduct = [];																																										// we create a new Array as a variable named "selectedProduct".
					angular.forEach($scope.product, function(x) {																																					// For each product of the "product" Array, 
						if (x.Quantity) {																																											// if product's "quantity" input field has a value (so, product's "checkbox" is checked anyway),
							selectedProduct.push(x);																																								// we add the "selected product" to the "selectedProduct" Array.	
						}
					})
					if (selectedProduct.length<1) {																																									// If no "product" has been selected from the "product" Array,
						evt.submitPrevented = true;																																									// an event handler prevents the form from being submitted by setting the property "submitPrevented" to 'true'.
					} else {																																														// If at least one "product" has been selected from the "product" Array, 
						camForm.variableManager.createVariable ({																																					// we "create" (declare) a new "Local process variable"
							name: 'selectedProduct',																																								// named 'selectedProduct' and
							type: 'json',																																											// provide as type information 'json' used for serialization.
							value: selectedProduct																					 
						});	
					}
				});
			</script>

If you could check what happens, please let me know :slightly_smiling_face:

Thank you very much for your time,
Steve

Hi @steftriant ,

can you check what scope aggregatedList has? You can find it in the cockpit process instance view.

Try adding a variables Out Mapping for your Subprocess (Modeler>CallActivity>Variables).

KR
Martin

Hi @martin.stamm,

My merged JSON variable (“aggregatedList”) has Main Process’s Instance scope.

Based on here (Call Activity | docs.camunda.org), I set the “Out Mapping” in Variables Tab of my Multi-Instance Call Activity to “All” (as I had already done with the “In Mapping”) but my relative task form (“Receive the Selected Products”) still remains empty from values :neutral_face:
On my inspection in Cockpit, I also saw that the “aggregatedList” variable is empty after my process’s running.

As far as the created (via camForm function) json variable (“selectedProduct”) is concerned, it’s being overwritten after each form submission and it has only kept last instance’s submitted values.

I also uploaded here my last updated bpmn file in case you could check sth :slightly_smiling_face:

orders.bpmn (8.0 KB)

Many thanks,
Steve

Hi @steftriant,

Is the variable only empty after the Subprocess finished? Can you confirm that values are added to aggregatedList correctly after Select Products is executed?

Line 36 in the Select Products script creates a new variable, which overrides the current one. If you want to update the variable instead, use fetchVariable('customVariable') and variableValue('customVariable', fieldValue);

Hi @martin.stamm and sorry for my delayed response,

No, my merged JSON variable (“aggregatedList”) remains empty on each completion of local instance. So, the submitted values of local instances (from the “Select Products” task) aren’t added in “aggregatedList” variable for some reason :neutral_face:

Could you please explain where and how must I write the above script ?
In the script of my called html file for the (“Receive the Selected Products”) task form, I’ve written the following:

<script cam-script type="text/form-script">
				var variableManager = camForm.variableManager;

				camForm.on('form-loaded', function() { 														// No any variable has been loaded from the server.
					variableManager.fetchVariable('aggregatedList');										// We declare the merged JSON variable 'aggregatedList' so as to fetch its values.
				});

				camForm.on('variables-fetched', function() {												// The merged JSON variable 'aggregatedList' has been fetched from the server.
					var variableValue = variableManager.variableValue('aggregatedList');
				});
			</script>

As ‘custom variable’, which variable must I write ?
The merged json (“aggregatedList”) in which are added the submitted values from all the completed local instances ?
Or the created via camForm function (upon the submission of each local instance form) json which includes the submitted values for each instance only ?

Thanks a lot,
Steve

Hi @steftriant,

That depends on how you want to achieve your goal. I can’t give you an definitive answer, as there are many ways to solve your problem.
Here is what I would do:

In the Embedded Form

  • Fetch Task Info to get access to ‘assignee’
  • fetch aggregatedList (you already do this)
  • on(‘submit’):
// Do your aggregation of Product
// ...

aggregatedList[$scope.task.assignee] = selectedProduct;
variableManager.variableValue('aggregatedList', aggregatedList);

This avoids a variable which only acts as a buffer and your logic which expands the list is all at one place.

Let me know if this help.

KR
Martin

Hi @martin.stamm,

I paste here the scripts from my 2 embedded html forms in order to check if I’m mistaken somewhere.

Select Products” Multi-Instance task

camForm.on('submit', function(evt) {																																								// Before "submit" request is sent to the server,
					var selectedProduct = [];																																										// we create a new Array as a variable named "selectedProduct".
					angular.forEach($scope.product, function(x) {																																					// For each product of the "product" Array, 
						if (x.Quantity) {																																											// if product's "quantity" input field has a value (so, product's "checkbox" is checked anyway),
							selectedProduct.push(x);																																								// we add the "selected product" to the "selectedProduct" Array.	
						}
					})
					if (selectedProduct.length<1) {																																									// If no "product" has been selected from the "product" Array,
						evt.submitPrevented = true;																																									// an event handler prevents the form from being submitted by setting the property "submitPrevented" to 'true'.
					} else {																																														// If at least one "product" has been selected from the "product" Array, 
						camForm.variableManager.createVariable ({																																					// we "create" (declare) a new "Local process variable"
							name: 'selectedProduct',																																								// named 'selectedProduct' and
							type: 'json',																																											// provide as type information 'json' used for serialization.
							value: selectedProduct																					 
						});	
					}
				});

Receive the Selected Products” task

<script cam-script type="text/form-script">
				var variableManager = camForm.variableManager;

				camForm.on('form-loaded', function() { 														// No any variable has been loaded from the server.
					variableManager.fetchVariable('aggregatedList');										// We declare the merged json variable 'aggregatedList' so as to fetch its values.
				});

				camForm.on('variables-fetched', function() {												// The merged json variable 'aggregatedList' has been fetched from the server.
					aggregatedList[$scope.task.assignee] = selectedProduct;
					var variableValue = variableManager.variableValue('aggregatedList', aggregatedList);
				});
			</script>

So, I ran again my process but the problem still remains. The “aggregatedList” is empty after the completion of each instance in Cockpit :neutral_face:
Could you please confirm that the above 2 scripts are correct ?
I didn’t modify sth in the 1st one (“Select Products”) but I’ve modified the 2nd one (“Receive the Selected Products”) based on your approach here.

I’m also wondering if my difficulty has to do with Modeler.
When I hadn’t added a variable in Out Mapping of Variables tab in my Call Activity, the “aggregatedList” was updated each time one user selected products in the Subprocess. Should it be empty or should I try to change the type (for example to “Source” instead of “All” that is now) ?

Thanks a lot and sorry for my many questions here.

Kind regards,
Steve

Hi again @martin.stamm,

I decided to remove the “Out Mapping” Variable in my Multi-Instance Call Activity and I ran again my process.
The result was that my merged json variable (“aggregatedList”) is fetched in Cockpit (as I can see all the submitted values from the Subprocess) but it seems that some problem still exists concerning the display of the same variable (“aggregatedList”) in the next task form of Taskllist as the form can’t be loaded there :neutral_face:

Could you please check the following script (from my embedded html file) which concerns the above task form (in which I want to display the “aggregatedList” variable) ?

<script cam-script type="text/form-script">
				var variableManager = camForm.variableManager;

				camForm.on('form-loaded', function() { 														// No any variable has been loaded from the server.
					variableManager.fetchVariable('aggregatedList');										// We declare the merged json variable 'aggregatedList' so as to fetch its values.
				});

				camForm.on('variables-fetched', function() {												// The merged json variable 'aggregatedList' has been fetched from the server.
					aggregatedList[$scope.task.assignee] = selectedProduct;
					var variableValue = variableManager.variableValue('aggregatedList', aggregatedList);
				});
			</script>

Thank you a lot :slightly_smiling_face:
Steve