Hi Marco,

I think your problem might be to do with having declared 'TotalMatrix' as a transient property, it does not exist in the 'Relations' table. In my example, I referenced a table class called 'ZenTutorial.PhoneNumber' but my SQL statement did NOT refer to a 'TotalPhones' column.

Consider this

colExpression="(select count(*) from ZenTutorial.PhoneNumber where Contact=ZenTutorial.Contact.ID)"

So the result of the SQL goes into my TotalPhones column on my tablePane but the ZenTutorial.PhoneNumber class does not recognise it as a SQL field. Transient properties are useful when opening %Persistent objects and dynamically setting the transient property at the %OnOpen() event based on a set of conditions. As TotalPhones is marked as transient, it is not stored in the Global Mapping for %Library.CacheStorage. You cannot reference it in your SQL statement.

What if you tried something like

SELECT COUNT(*) AS TotalMatrix FROM Relations

You will need all those relationships in there though for it to work. If you have a <column> section defined in your tablePane be sure to set colName="TotalMatrix"

I hope that helps.

To summarise, the answer is twofold:

1) Use the OnCreateResultSet event of the tablePane to get your filter value and pass it into your custom SQL. I have appended any filter values onto the end of my WHERE clause in each SQL fragment.

2) Use the 'onrefresh' event of the tablePane to call JavaScript to hide other ZEN components when the table is updated.

This is excellent! Probably the best answer on this question. I would add that sometimes the process is the login session of an individual user and what the user does within that session needs to be carefully managed!

You can also use set $ZS=1234567 to set the max-limit memory for your process. Useful if the default memory is inadequate for a particular process but is fine for everything else.

Thanks Wolf Koelling.  I should have made it more explicit that %vars are shared across NAMESPACES not processes. When a user logs into our system their process remains active as long as they are logged in. During their login session they can call any number of COS routines to perform a wide range of different functions. Variables not explicitly killed off still reside in memory and this was the problem I had to solve. 

I had to be sure that

a) The %ZLOG COS routine would not crash out because of missing variables the routine expects.

b) All the calls or entry points into %ZLOG would still work as normal otherwise a system-wide crash would occur across all our databases.

c) Be able to identify that if my background process had called %ZLOG then use the PPG created before the call to %ZLOG. No other code in our system uses PPGs whereas there is a plethora of globals, %variables and non-percent variables. 

I could have used a scratch or temporary global such as ^CacheTempUser.DataExtract($JOB). This type of global is killed off when the instance is brought down for our daily backup job. A PPG was very easy to implement.

I have placed a strikethrough on the erroneous statement in my answer.

Lets say I have a *.INT routine called %ZLOG. This routine can be called from any namespace and when it is called,  some variables are already defined. I want to check for the existence of a specific PPG before overwriting a collection of variables set by something further up the call stack. If the PPG I created exists then I can safely assume that it was my process that set it.

eg. Before calling %ZLOG, set a PPG in your process then at the start of %ZLOG check for the existence of the PPG.

// Assume variables are set by something else if PPG not present in %ZLOG
Set var1=$GET(var1)
Set var2=$GET(var2)
Set: $DATA(^||ExtractProcess("var1")) var1=^||ExtractProcess("var1") 
Set: $DATA(^||ExtractProcess("var2")) var2=^||ExtractProcess("var2")

PPGs are cleared when the process is terminated so you don't have to kill them off. So if you plan to run something as a background job or have some kind of unique transaction taking place for an individual user's process, PPGs are good to use. In this example, I have opted to use a PPG rather than using the NEW keyword. NEW is only useful if you know what variables to NEW and what not to NEW. At least with a unique PPG I know for a fact that it was my ExtractProcess that created the PPG. %VAR variables are accessible for any namespace but are not specific to a particular process.

Ok, I *think* I have it now.

ClientMethod onSelectRow() [ Language = javascript ]
{
	var resultsPane=zenPage.getComponentById('resultsPaneId');
	var isHidden=resultsPane.getHidden();

	if (!isHidden)
	{
		var table=zenPage.getComponentById('workBioTable');
		var dataRow=table.getRowData(table.selectedIndex);


	if (dataRow)
	{
		var rowID = zenPage.GetSelectedRowId(dataRow.RequestDate,dataRow.Worksheet,dataRow.SpecId);

		if (rowID)
		{
			this.populatePIDForm(dataRow);
			var waittoend = zenPage.GetAuditLogs(rowID);
			this.populateAuditEvents();
		}
	}


} // end-if
}

Note the variable 'waittoend' and the zenPage.GetAuditLogs(RowId) method. This process builds the global that the secondary tablePane is built from. Without the return value, it is possible for the table to refresh before the global is built for the newly selected row.

Testing this has been difficult because in the most cases, the table was displayed correctly but several dozen clicks later you gasp, "Did I just see that!?" when you notice that on this one occasion it did not update correctly.

Edit: Ok, so I THOUGHT I answered my own question. Read up on asynchronous and synchronous modes via the online documentation.  Here's my "solution".

ClientMethod onSelectRow() [ Language = javascript ]
{
	// Set mode
	var zenSynchronousMode='true'

	// Do some synchronous stuff

	// Turn off mode when done
	zenSynchronousMode='false'
}

It hasn't worked :(