Edit grid rows in subforms

From pmusers
Jump to: navigation, search

This process shows how to open a subform to edit the fields of a grid row in another form.

Process: Edit_grid_rows_in_subforms-1.pmx (right click and select Save link as)
Author: Amos Batto (amos@processmaker.com)
Version: 1.0 (2018-05-07)
Tested in: ProcessMaker 3.2.1 Community in Debian 8.4 with Firefox 51

The process consists of two DynaForms. The first DynaForm is entitled "Chemical List" and it contains the "chemicalList" grid, which holds information about different chemicals.


This form has the following fields:

  • a grid whose ID and variable is "chemicalList". It has the following fields:
    • text field with the ID "id"
    • text field with the ID "chemicalName"
    • textarea field with the ID "chemicalDescription"
    • text field with the ID "chemicalPrice"
    • MultipleFile field with the ID "specsFile"
  • a hidden field with an ID and variable named "submitAction", which holds the ID of grid row to be edited when the form is submitted,
  • a "submit" button. When this button is clicked, the user is done editing the list of chemicals and will move onto the next task in the process.

When the user clicks on the "edit" checkbox in each grid row, a second DynaForm entitled "Chemical Details" is opened to edit the details about the chemical in that grid row:


When the user clicks on the "Submit" button in this subform, the data in this DynaForm will be saved in the grid and the user will be redirected back to the first "Chemical List" form to continue editing it.

In order to implement this functionality, add the following JavaScript code to the "Chemical List" DynaForm:

//clear submit action to reset form when it loads

//if an empty grid, then delete the first row:
if ($("#chemicalList").getNumberRows() == 1 && $("#chemicalList").getValue(1,1) == '') {
else {
  //if the grid already exists, then set the click handlers for the
  //"edit" checkboxes in each row:
  var nRows = $("#chemicalList").getNumberRows();
  for (var i=1; i <= nRows; i++) {
    $("[id='form[chemicalList]["+i+"][edit]']").click( function() {
      var rowNo = this.id.match(/\]\[(\d+)\]\[/)[1];
      var chemicalID = $("[id='form[chemicalList]["+rowNo+"][id]']").val();
      $("#submitAction").setValue( chemicalID );
      $( "#"+$("form").prop("id") ).submitForm();
    } ); 

//when adding a new row to the grid, set the new row's chemical ID and 
//set the click event handler for the "edit" checkbox:
$("#chemicalList").onAddRow( function(aNewRow, oGrid, rowIndex) {
  //set the chemical ID to the current time stamp:
  var now = new Date().getTime();
  //where "id" is the first field in row:
  //set click event handler in the "edit" checkbox to submit form and 
  //open the Chemical Details subform.
  //change [5] to the column number of the "edit" checkbox:
  aNewRow[5].$el.find("input[type=checkbox]").click( function() {
    $( "#"+$("form").prop("id") ).submitForm();
  } );

First, this code resets the value of the "submitAction" field to "" (an empty string), so it will not effect the next time the form is submitted. If the grid is empty, then it removes the first row in the grid, which is created by default.

If the grid, already had rows of chemicals in it, then the code loops through each row in the "chemicalList" grid and adds a click event handler function to each "edit" checkbox. When clicked, the ID of the clicked grid row is set in the "submitAction" field and then the DynaForm is submitted. A subsequent trigger will use the value in the "submitAction" variable to find out which grid row needs to be edited in the "Chemical Details" subform.

The JavaScript code also sets the value of each new row's "id" field to the current timestamp. Using the timestamp ensures that each new row has a unique value in its "id" field, which will can be used to identify the row, regardless of whether the row number changes due to deletions and additions to the grid.

Notice that if fields are deleted or added to the grid, then the column number of the "edit" checkbox may no longer be [5], so it needs to be changed in this line of code:

  aNewRow[5].$el.find("input[type=checkbox]").click( function() {

The process uses two triggers to set the values in the forms. The first trigger is named "Set variables in the 'Chemical Details' form" and it fires after the "Chemical List" form. It transfers the values in the grid row whose "edit" checkbox was clicked to the "Chemical Details" form so they can be edited by the user:

$fieldName = "specFiles"; //set to the ID of the MultipleFile field 

//reset form if it was edited before:
@@chemicalID          = '';
@@chemicalID_label    = '';
@@chemicalName        = '';
@@chemicalName_label  = '';
@@chemicalDescription = '';
@@chemicalDescription_label = '';
@@chemicalPrice       = '';
@@chemicalPrice_label = '';
@=specFiles           = array();

if (!empty(@@submitAction)) {
	$id = @@submitAction;
	//search for the chemical:
	foreach (@=chemicalList as $aRow) {
		//if ID is found then set the variables in the Chemical Details form
		if ($aRow['id'] == $id) {
			@@chemicalID                = $aRow['id'];
			@@chemicalID_label          = $aRow['id_label'];
			@@chemicalName              = $aRow['name'];
			@@chemicalName_label        = $aRow['name_label'];
			@@chemicalDescription       = $aRow['description'];
			@@chemicalDescription_label = $aRow['description_label'];
			@@chemicalPrice             = $aRow['price'];
			@@chemicalPrice_label       = $aRow['price_label'];       
			@=specFiles                 = $aRow['specFiles'];         //MultipleFile field
			$oDoc = new AppDocument();
			foreach(@=specFiles as $aFile) {
				$aFileInfo = $oDoc->Load($aFile['appDocUid'], $aFile['version']);
				$aFileInfo['APP_DOC_FIELDNAME'] = $fieldName;
	if (empty(@@chemicalID)) {
		throw new Exception("Unable to find chemical ID '$id'.");

If the @@submitAction variable is empty, then the user clicked on the "Submit" button, so the user wants to stop edit the list of chemicals. On the other hand, if the @@submitAction variable is not empty, then it has the ID of a grid row that needs to be edited in a separate subform. The code loops through the @=chemicalList grid to find the row with the matching "id" field and then transfers the values from the grid row to the variables used by the "Chemical Details" form, so they can edited in the next step by the user.

When the user finishes editing the data in the "Chemical Details" subform, then a second trigger entitled "Redirect to Chemical List form" is fired to transfer the edited details back to the "chemicalList" grid, so they can be redisplayed to the user in the first form. The PMFRedirectToStep() function is used to redirect the case back to the "Chemical List" DynaForm, so the user can continue editing the list of chemicals. Note that this function does NOT work correctly in the ProcessMaker Mobile App, so users with mobile devices should access their cases using the "Mobile" User Experience in the web browser of their mobile devices.

Here is the code of the second trigger, which is set to fire after the "Chemical Details" DynaForm:

//set to the ID of the Dynaform holding the "chemicalList" grid:
$dynaformID = '9817252625ab426dbee2bb7040678137';

//search for chemical ID in the "chemicalList" grid and 
//set values in the row:
$found = false;

for ($i = 1; $i <= count(@=chemicalList); $i++) {
	if (@=chemicalList[$i]['id'] == @@chemicalID) {
		@=chemicalList[$i]['name']              = @@chemicalName;
		@=chemicalList[$i]['name_label']        = @@chemicalName_label;
		@=chemicalList[$i]['description']       = @@chemicalDescription;
		@=chemicalList[$i]['description_label'] = @@chemicalDescription_label;
		@=chemicalList[$i]['price']             = @@chemicalPrice;
		@=chemicalList[$i]['price_label']       = @@chemicalPrice_label;
		@=chemicalList[$i]['specFiles']         = @=specFiles;             //multipleFile field
		$found = true;
		//set the field name to the row number of the grid in the database record for the MultipleFile:
		$fieldName = "[chemicalList][$i][specFiles]";
		$oDoc = new AppDocument();
		foreach(@=specFiles as $aFile) {
			$aFileInfo = $oDoc->Load($aFile['appDocUid'], $aFile['version']);
			$aFileInfo['APP_DOC_FIELDNAME'] = $fieldName;
		$found = true;	

if (!$found) {
	throw new Exception("Chemical ID '".@@chemicalID."' not found in list.");

PMFRedirectToStep(@@APPLICATION, @%INDEX, 'DYNAFORM', $dynaformID);

This trigger code loops through the rows in the @=chemicalList grid variable to find the row with the "id" which is equal to the @@chemicalID field in the "Chemical Details" form. Then, it transfers the data from the edited fields in the "Chemical Details" form to the grid row with the matching "id" field.

If there are files that need to be transferred from a MultipleFile field in the "Chemical Details" form to a MultipleFile field in "chemicalList" grid, then not only does the array holding information about the files needs to be transfered to the grid, but the database record for each file needs to be updated in the APP_DOCUMENT table in the workspace's database. The AppDocument::Load() and AppDocument::update() methods are used to change the value in the APP_DOCUMENT.APP_DOC_FIELDNAME field in the database from specFiles (which is the DynaForm's field's name) to [chemicalList][row-number][specFiles], which is the field's name in the grid. That way the files will appear inside the grid.

When a user runs a case, she can edit the list of chemicals in the "Chemical List" form:


If she decides that it would be easier to edit the details of a chemical in a separate form, she can then click on the "edit" checkbox to open the "Chemical Details" subform:


She then changes the details about the chemical in the subform:


After clicking on the subform's "Submit" button, the second trigger will be fired which redirects back to the first Dynaform containing the updated details in the list of chemicals in the grid: