tablePane: After calling executeQuery() rowCount() returns zero, getRowData(0) returns null, selectRow(0) does not select a row.

292 views
Skip to first unread message

Jay Ayliff

unread,
Sep 26, 2014, 5:18:49 AM9/26/14
to intersys...@googlegroups.com
I have a tablePane control and a dataCombo control in a modal group on a Zen form. When the user selects an item from the dataCombo a client method is called which sets a parameter for the tablePane and requeries it using the executeQuery. This all works.

What I want to next in the JavaScript is select the first row in the table Pane, but I cannot get this to work. Immediately after the executeMethod is called:
selectRow(0) does not cause the first row to be selected
rowCount() returns zero.

The query does return data, and one or more rows are displayed in the tablePane. What I need to do is programmatically select the first row.
Questions:
- Is the behaviour of rowCount() and selectRow() a fault or am I using the control incorrectly?
- How do I programmatically select the first row in the tablePane?

tablePane definition is as follows:

<tablePane id="tablePaneLocation"
label="Location and Group"
fixedHeaders="true"
showZebra="true"
queryClass="Tools.Edit.GiraffeMain"
queryName="FindLocs"
valueColumn="ID"
rowSelect="true"
useSnapshot="false"
>
<column colName="ID" hidden="true" />
<column colName="locCode" hidden="false"/>
<column colName="locName" hidden="false"/>
<column colName="locGroup" hidden="false"/>

<parameter id="userForGroupsTP" value="0"/>

</tablePane>

Here's the JavaScript, which fires when the dataComboUser value changes:
{
    var userCombo = zenPage.getComponentById('dataComboUser');
    var locTP = zenPage.getComponentById('tablePaneLocation');
    var locParaTP = zenPage.getComponentById('userForGroupsTP'); // This is the parameter for the query
    if (userCombo.getAuxValue()!='') {
        locParaTP.value = userCombo.getAuxValue(); // Set the parameter for the query (user ID)
        locTP.executeQuery(); // execute the query - this actually works
        locTP.selectRow(0,true,true);        // - this does not work, even though there is at least one row returned
    }
}

Vlado

unread,
Sep 26, 2014, 11:11:24 PM9/26/14
to intersys...@googlegroups.com
Hi Jay,
Use selectedIndex insted:(0 based)
--------------------------------------------------------------------------------------------------------------
var index=0
var action new Function("zen('table').setProperty('selectedIndex','"+index+"' );");
    zenSetDeferredAction(action,200);
--------------------------------------------------------------------------------------------------------------
===============================================================
Class ZENTest.DataComboTablePaneSelectRow Extends %ZEN.Component.page
{

/// This XML block defines the contents of this page.
XData Contents [ XMLNamespace = "http://www.intersystems.com/zen]
{
<page xmlns="http://www.intersystems.com/zentitle="" >
<dataCombo id="City"
label="City" 
    name="dataCombo"
editable="true"
searchKeyLen="10"
valueColumn="2"
maxRows="1000"
dropdownWidth="20.0em"
displayColumns="2"
sql="SELECT ID,Home_City FROM ZENDemo_Data.Employee WHERE Home_City %STARTSWITH GROUP BY Home_City ORDER BY Home_City"
sqlLookup="SELECT Home_City FROM ZENDemo_Data.Employee WHERE Home_City = ?"
onchange="zenPage.notifyOnChange(zenThis.getValue());>
</dataCombo>
<spacer height="10"/>
<tableNavigator tablePaneId="table"/>
<tablePane id="table" width="600"
        caption="This is a tablePane" 
queryClass="ZENDemo.Data.Employee"
    queryName="ListEmployees"
        useSnapshot="true" 
        autoExecute="true" 
        showZebra="true" 
        pageSize="10" 
        showRowNumbers="true" 
        valueColumn="ID" 
        maxRows="1000" >
<parameter id="parameter"/>
</tablePane>
</page>
}

ClientMethod notifyOnChange(value) [ Language = javascript ]
{
var ctrl zen('table');
ctrl.setProperty('parameters',1,value);
var index=0
var action new Function("zen('table').setProperty('selectedIndex','"+index+"' );");
    zenSetDeferredAction(action,200);
}

}
===================================================================
**Владо

peter cooper

unread,
Sep 27, 2014, 6:22:38 AM9/27/14
to intersys...@googlegroups.com

Hi Jay

 

The problem, I think, is that the execute() is async so it goes off an takes some time to execute – and so the JS does not stop so the selectRow() fires while the table is empty :{

 

What is needed is for ISC to add a call back that fires when the table execute has finished

 

I have done this myself by sub-classing the tablepane

Class Component.TablePane Extends %ZEN.Component.tablePane [ System = 3 ]
{

Parameter NAMESPACE = "http://www.xisltd.net/zenapp";

Property onRefreshContentsUser As %ZEN.Datatype.eventHandler;

ClientMethod onRefreshContents() [ Language = javascript ]
{
            this.invokeSuper('onRefreshContents',arguments);
            if ('' != this.onRefreshContentsUser) {
                        go zenInvokeCallbackMethod(this.onRefreshContentsUser,this,'onRefreshContentsUser');
            }

 

that I have hooked into the built in onRefreshContents to add the call-back

I have to invoke the super ‘cos this stops the progress bar

 

 

So I can then have

<xis:tablePane

                …

                …

                onRefreshContentsUser=”zenPage.do_some_fiddling_with_the_table()”

/>

 

= =

You also need to be aware that setting the selected row will fire the onrowselect, is there is one, which may have undesired consequences

 

:}

 

Peter

--
You received this message because you are subscribed to the Google Groups "InterSystems: Zen Community" group.
To post to this group, send email to InterSys...@googlegroups.com
To unsubscribe from this group, send email to InterSystems-Z...@googlegroups.com
For more options, visit this group at http://groups.google.com/group/InterSystems-ZEN?hl=en
Zen Community Terms and Conditions: http://groups.google.com/group/InterSystems-ZEN/web/community-terms-and-conditions
---
You received this message because you are subscribed to the Google Groups "InterSystems: Zen Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email to intersystems-z...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Vlado

unread,
Sep 27, 2014, 11:55:44 PM9/27/14
to intersys...@googlegroups.com
In my example "selectedIndex" only highlight the first row(it doesn't select it).
So you need also:
 locTP.selectRow(0); 
if you want truly to select the first row.
**Владо

Jay Ayliff

unread,
Sep 29, 2014, 6:46:20 AM9/29/14
to intersys...@googlegroups.com
Hi Peter and Vlado,

Thanks for your replies. Vlado, I guess the important line of code in your solution is this:
    zenSetDeferredAction(action,200);
...which is waiting 200ms for the query to to complete.

Peter, I implemented your solution by subclassing tablePane and adding the onRefreshContents method definition. I implemented this in the main class as follows:

/// Callback function for TablePane - fires when the query has executed.
ClientMethod mgLocationTPRefresh(thisComponent) [ Language = javascript ]
{
     alert('rowcount=' thisComponent.rowCount); // always displays zero
    thisComponent.selectRow(0,true); // selects the first row
    }
}

Interestingly, the alert call also displays zero, and if I add a call to getRowData(0) this also returns null. But the call to selectRow() does successfully select the row, which appears highlighted when the script has run. While doing this I saw there was an existing definition for an onrefresh callback function. I switched back to the original tablePane from the subclassed one, pointing the onrefresh to the same JavaSCript function as for onRefreshContentsUser, and it worked exactly the same - rowCount and getRowData behave as if there are no rows in the table, while selectRow() selects the row the previous line didn't think existed. I've tried with a variety of numbers, and selectRow will select the row if it exists, and does nothing (but doesn't throw an error) if the row doesn't exist.

The selectRow() call only worked from the onrefresh/onRefreshContentsUser callback, but not from the script that called executeQuery, indicating that it is async as you said.

It's working as well as I need it to for this application, but I think someone ought to get rowCount and getRowData() working properly.

Best regards

Jay Ayliff

peter cooper

unread,
Sep 30, 2014, 3:16:07 AM9/30/14
to intersys...@googlegroups.com

Hi Jay

 

A Sync *can*work but it depends how long the query takes – in your example if it takes longer than 200 millisec then it will fail.

Never had to fiddle with the rowcount so can’t comment

 

Glad you have got it working

 

Peter

--

Jay Ayliff

unread,
Sep 30, 2014, 4:45:20 AM9/30/14
to intersys...@googlegroups.com, p...@xisltd.net
Hi Peter,

Yes, the problems with using a timer to wait for a query to finish are threefold:
a) If the query takes longer than the timeout then your code fails
b) If you set the timeout extra long to make sure then you waste time.
c) When you implement it live you find the query takes WAY longer than it did on your development machine and it fails anyway!

For these reasons it's always best to use a call back if possible, like the existing onrefresh. Like I said, it's working well enough for me because all I need to do is select the first row if there is one. It would be nice if the rowCount and getRowData() actually worked as documented though.

Jay

Jay Ayliff

unread,
Sep 30, 2014, 6:33:26 AM9/30/14
to intersys...@googlegroups.com
I was going to raise a ProdLog for this, and created the class below to be used in the SAMPLES namespace to demonstrate. But in the code below both selectRow() and rowCount both work. Not sure what to do next as I'm unable to replicate the problem except in the midst of a large application.

/// Created using the page template: Default
Class ZENTest.TablePaneTest Extends %ZEN.Component.page
{

/// Class name of application this page belongs to.
Parameter APPLICATION = "ZENTest.TestApplication";

/// Displayed name of this page.
Parameter PAGENAME;

/// Domain used for localization.
Parameter DOMAIN = "ZENTEST";

/// This Style block contains page-specific CSS style definitions.
XData Style
{
<style type="text/css">
</style>

}

/// This XML block defines the contents of this page.
XData Contents [ XMLNamespace = "http://www.intersystems.com/zen]
{
<page xmlns="http://www.intersystems.com/zentitle="">

<text id="textbox" label="Initial Letter" value="A" />
<button caption="Load Table" onclick="zenPage.loadTable()id="button" />

<tablePane id="table" width="600"
        caption="This is a tablePane"
        showZebra="true" 
        pageSize="20" 
        showRowNumbers="true" 
        maxRows="1000" 
        sql="select Name, Title from ZENDemo_Data.Employee where Name %Startswith ?"
        onrefresh="zenPage.tableCallBack(zenThis)"
>
<parameter id ="queryparm" />
</tablePane>
</page>
}

ClientMethod loadTable() [ Language = javascript ]
{
var textbox zenPage.getComponentById('textbox');
var tablepane zenPage.getComponentById('table');
var sql 'select Name, Title from ZENDemo_Data.Employee where Name %Startswith ?';
var parm=zenPage.getComponentById('queryparm');
parm.value=textbox.value;
//alert(sql);
    //tablepane.sql=sql;
tablepane.executeQuery(); // Execute the query asynchronously
}

/// This calback function is called when the tablepane query finishes
ClientMethod tableCallBack(thisComponent) [ Language = javascript ]
{
alert ('Rowcount is ' thisComponent.rowCount); // This works, returning 20+
thisComponent.selectRow(0); // This works too
}

}



Vlado

unread,
Sep 30, 2014, 9:40:03 PM9/30/14
to intersys...@googlegroups.com
"If this value is set by the callback, upon return the rowCount property contains the number of rows returned by the query. After the query is executed, rowCount could be different from rows.

Note that rowCount is a string, and not numeric, as its value might be "" or "100+". Any number of rows greater than 100 is represented as "100+". When testing rowCount from JavaScript, if you want to convert to a numeric value use parseInt for base 10:

Jay Ayliff

unread,
Oct 1, 2014, 4:09:33 AM10/1/14
to intersys...@googlegroups.com
Hi Vlado,

Thanks for finding that documentation page. Yes, this is how it is supposed to work, and this is how the little standalone page I wrote works. But it is not how my original code works - there the rowCount returns zero every time. I now need to analyse both pages to see which difference causes the difference in behaviour.

Best regards

Jay Ayliff

Derek Day

unread,
Oct 1, 2014, 8:37:34 AM10/1/14
to intersys...@googlegroups.com

Are you using a custom query, on Execute or somesuch? If so, are you filling in the queryInfo object?

 

~Derek

 

From: intersys...@googlegroups.com [mailto:intersys...@googlegroups.com] On Behalf Of Jay Ayliff
Sent: Wednesday, October 01, 2014 4:10 AM
To: intersys...@googlegroups.com
Subject: [InterSystems-Zen] Re: tablePane: After calling executeQuery() rowCount() returns zero, getRowData(0) returns null, selectRow(0) does not select a row.

 

Hi Vlado,

 

--

Jay Ayliff

unread,
Oct 1, 2014, 8:43:03 AM10/1/14
to intersys...@googlegroups.com
Hi Derek,

Interesting question. It's not a custom query but it is a Union query and does include a call to a SqlProc method. Would that make a difference? Here's the code:

Query FindLocs(UserID As %String) As %SQLQuery(CONTAINID = 1, ROWSPEC = "ID,Loc Code,Loc Description,Group")
{
SELECT Tools_Edit.GiraffeMain_UserKey(:UserID, SSUSR_Group, SSUSR_Profile, SSUSR_CTLAN_DR) as rowkey,
SSUSR_DefaultDept_DR->CTLOC_Code, SSUSR_DefaultDept_DR->CTLOC_Desc || ' [Main]', SSUSR_Group->SSGRP_Desc
FROM SQLUser.SS_User
WHERE %ID = :UserID
UNION
SELECT Tools_Edit.GiraffeMain_UserKey(:UserID, OTHLL_UserGroup_DR, OTHLL_Profile_DR, (SELECT SSUSR_CTLAN_DR FROM SQLUser.SS_USER WHERE %ID = :UserID)) as rowkey,
OTHLL_CTLOC_DR->CTLOC_CODE, OTHLL_CTLOC_DR->CTLOC_Desc, OTHLL_UserGroup_DR->SSGRP_Desc
FROM SQLUser.SS_UserOtherLogonLoc
where OTHLL_ParRef = :UserID
}
/// Compile a compound key for the FindLocs query
ClassMethod UserKey(UserID As %String, GroupID As %String, ProfileID As %String, LanguageID As %String) As %String [ SqlProc ]
{
If LanguageID="" {
config=$g(^websys.ConfigurationD(1))
Language=$lg(config,15) // LanguageApp
}
Else {
Language=LanguageID
}
UserID_"^"_GroupID_"^"_ProfileID_"^"_Language
}

Derek Day

unread,
Oct 1, 2014, 9:44:00 AM10/1/14
to intersys...@googlegroups.com

I think I wasn’t clear – (and after doing some review, my comment probably was not as relevant as I thought it was anyway). I have another draft answer that now seems to be completely irrelevant. Let’s try again…

 

You should be able to reproduce the problem with a simple query that is executed with different parameters from the page after the page has already been rendered. If you still want to report this to development(prodlog) please add me to the cc list and reply to the list with the prodlog reference , otherwise, I’d be happy to create the bug report.

 

Analysis:

 

rowCount *should* always be set by the time onRefreshContents() and then the onrefresh callback is called.

 

Unfortunately the server DOM changes have not yet been copied to the client because the data is fetched, the html is drawn, and the callbacks are made all from one place before the Zen Engine gets a chance to do any DOM updates.. This causes a problem when refreshing a query because the callbacks are invoked before the server performs the object synchronization, and %DrawTable updates the ..rowCount serverside property without setting the client property, so the client side callbacks don’t see the new property values.

The tablePane onRefreshContents(), onrefresh logic therefore cannot make use of the client rowCount property without using zenDeferred (or some other queuing mechanism) being used.

 

I think that the onRefresh callbacks, should probably be moved to the renderContents mechanism (which is where client-side rendering normally occurs), since this is called *after* DOM synchronization, alternatively the %DrawHTML code would have to set the client properties directly, or pass them as arguments to onRefreshContents() and the onrefresh callback.

 

Or – Zen applications should use a client-side table widget and use the jsonSQLProvider to get the data!

 

~Derek

 

From: intersys...@googlegroups.com [mailto:intersys...@googlegroups.com] On Behalf Of Jay Ayliff
Sent: Wednesday, October 01, 2014 8:43 AM
To: intersys...@googlegroups.com
Subject: [InterSystems-Zen] Re: tablePane: After calling executeQuery() rowCount() returns zero, getRowData(0) returns null, selectRow(0) does not select a row.

 

Hi Derek,

--

Jay Ayliff

unread,
Oct 2, 2014, 4:31:06 AM10/2/14
to intersys...@googlegroups.com
Hi Peter,

I get ti now. I re-ran the code I posted on 30th Sep (repeated below):
1. On load the table is populated with 20 rows
2. Enter "Vanzetti" in Initial Letter, click Load Table. Alert says 20+ rows, six are displayed.
3. Enter "Vanzetti,C" in Initial Letter, click Load Table. Alert says 6 rows, 1 is displayed.
4. Enter "Smith" in Initial Letter, click Load Table. Alert says 1 row, 2 are displayed.

The rowCount property always shows the result of the previous query, which I think is consistent with what you said.

I'll have a go at the ProdLog.

Best regards

Jay Ayliff
Message has been deleted

Jay Ayliff

unread,
Oct 2, 2014, 5:22:42 AM10/2/14
to intersys...@googlegroups.com
Raised prodlog 126572

Derek Day

unread,
Oct 2, 2014, 12:13:46 PM10/2/14
to intersys...@googlegroups.com

Hi, Jay.

 

You might try the following:

1.       Define an onrender callback for your tablePane pointing at the original onrefresh logic

2.       Change the onrefresh logic to set the client render flag – hopefully this will cause Zen to invoke the original onrefresh logic after the change to the rowCount property has been synchronized

 

If that works, please update the bug report, since Zen could be modified to do this automatically (calling the onrefresh handler from renderContents() before invoking any existing onrender handler.

 

HTH,

Derek

 

From: intersys...@googlegroups.com [mailto:intersys...@googlegroups.com] On Behalf Of Jay Ayliff
Sent: Thursday, October 02, 2014 4:31 AM
To: intersys...@googlegroups.com
Subject: [InterSystems-Zen] Re: tablePane: After calling executeQuery() rowCount() returns zero, getRowData(0) returns null, selectRow(0) does not select a row.

 

Hi Peter,

Reply all
Reply to author
Forward
0 new messages