Saving User Input to Reports

This example uses a global event to intercept the standard save report functionality and prompt the user for some additional information, which is then saved alongside the report. This example makes multiple server callbacks; in fact, the action event itself is called multiple times with different arguments. Additionally, the event is stored within a .NET Assembly.

NOTE. This example could be written in a variety of ways. The entire code could be saved within the config file, for example. Or it could be split into multiple action events, each handling a separate case. The format is flexible and completely up to you.


This is the client-side javascript responsible for creating and handling a Dialog prompt for the user.

// Called upon clicking the save report button
function ShowSaveReportWindow(clientInfo, reportId, reportName, description)
	// Can create raw Html or an Html DOM element. LoadHtmlDialog will handle both cases.
	var html = "<div style='padding:15px'>";
	html += "<br /><br /><span>Fill in the fields below, then click OK to continue.</span>";
	html += "<br /><br /><br />";
	html += "<span>Report Folder \\ Name</span> <input id='reportName' type='text' />";
	html += "<br /><br />";
	html += "<span>Description</span> <input id='description' type='text' />";
	html += "<br /><br />";
	html += "<span>Other Info</span> <input id='other' type='text' />";
	html += "<br /><br /><br />";
	html += "</div>";

	// Create options argument; renderOnly tells Exago to keep visibility hidden, so that we can put html in DOM and set values as shown below.
	// Alternately, we could set values in our HTML above, and not set renderOnly to true.
	var options = {
		height: 300, width: 400, titleText: "Save Report", okCallback: function () { 			parent.OnOkSaveReport(clientInfo); },
		cancelCallback: function () { parent.OnCancelSaveReport(clientInfo); }, 				renderOnly: true, showButtons: true

	clientInfo.LoadHtmlDialog(html, options);

	// Set any values (for convenience).
	clientInfo.GetDialogElementById("reportName").value = reportName;
	clientInfo.GetDialogElementById("description").value = description;

	// Since we set renderOnly to true, we now need to show the dialog.

// Called upon click of the OK button in the dialog.
function OnOkSaveReport(clientInfo)
	// Validate the data.
	var reportName = clientInfo.GetDialogElementById("reportName").value;
	var description = clientInfo.GetDialogElementById("description").value;
	var other = clientInfo.GetDialogElementById("other").value;

	// Additional (non-report) data can be saved either by ajax call in host application, or by passing data to server via server callback.
	// This demonstrates passing complex data via Exago interface:
	var oth = { x: 'xData', y: 7 };
	var arr = ['a', 'b', 'z'];
	// Objects are automatically converted to json; We'll need to deserialize on server side.
	var obj = { reportName: reportName, description: description, other: oth, array: arr };

	// Now call back to the server to save the data.
	clientInfo.ServerCallback("SaveReport", "save", obj);

	// We can close the dialog right away or when save is completed after server callback.

// Called upon click of the Cancel button in the dialog.
function OnCancelSaveReport(clientInfo)


This is the server-side code which tells Exago how to handle the user input. It's stored in an assembly.

public class SaveReportDataOther
public string x;
public int y;

public class SaveReportData
public string reportName;
public string description;
public SaveReportDataOther other;
public string[] array;

public class ActionEvent
public static object SaveReport(SessionInfo sessionInfo, string actionType, string jsonData)
JavascriptAction jsAction = sessionInfo.JavascriptAction;
switch (actionType)
case null:
// Initialization. Happens when Exago is first loaded in order to set the javascript click event.
jsAction.SetJsCode("clientInfo.refreshDataOnReturn = false; clientInfo.ServerCallback(\"SaveReport\", \"show\");");
case "show":
// Happens when the save button is clicked. We're calling back to server since we don't have all the following session values.
jsAction.SetJsCode(String.Format("parent.ShowSaveReportWindow(clientInfo,\"{0}\",\"{1}\",\"{2}\");", sessionInfo.Report.Id, jsAction.EncodeString(sessionInfo.Report.Name), jsAction.EncodeString(sessionInfo.Report.Description)));
case "save":
// Called when the OK button is clicked in our custom dialog. Exago has not saved the report, but makes reportXml available to save as shown below.
// This describes how we can create complex data that gets mapped to a custom class definition.
SaveReportData data = Json.Deserialize(jsonData, typeof(SaveReportData)) as SaveReportData;
// Get the report xml to save.
string reportXml = sessionInfo.ReportObject.GetXml();
// Save the report. Report name and ID are embedded within reportXml, although we can also pass them in seperate fields or within our combined jsonData.
// We can alert the user of something here.
sessionInfo.JavascriptAction.SetJsCode("clientInfo.Alert(\"Report has been saved\");");
throw new Exception(String.Format("Invalid actionType: {0}", actionType));
return sessionInfo.JavascriptAction;
NOTE. For an additional action event example, see Responsive Dashboards.

Hidden Article Information

Article Author
Exago Development
created 2017-02-21 16:27:25 UTC
updated 2017-02-21 16:36:08 UTC

Action Events, Save, UDF,
Have more questions? Submit a request