tudor-malene / Easygrid

Grails plugin - simple and declarative way of defining a DataGrid
Apache License 2.0
27 stars 24 forks source link

How does subgrid link to grid in a jqgrid? #65

Closed ericraskin closed 10 years ago

ericraskin commented 10 years ago

As usual, here comes a long post. Trying to anticipate what you might need to answer the question. ;-)

I can't quite figure out how a subgrid links to a grid. I know you say it is like a master-detail grid in the docs, but I'm not getting it yet. In the master-detail grid, you put a masterGrid and a childParamName in the GSP, and then you put a GlobalFilter in to compare the parameter with the data. There is no place to put that for a subgrid in the GSP, so how do I pass a parameter for the subgrid? Here's my code:

GSP (yes, this particular grid is a detail grid for another one defined previously. This detail grid contains the subgrid)

      <grid:grid name="managerListOrders" masterGrid="managerList" childParamName="listId">
          <grid:set width="auto" height="auto" caption="Rental Orders"/>
      </grid:grid>

Controller:

    def managerListOrdersGrid = {
        dataSourceType 'gorm'
        domainClass Order
        idColName 'ordnum'
        subGrid 'managerListOrderDetails'
        globalFilterClosure{ params ->
            rentalList {
                eq('id', params.listId ? params.listId as long: -1l)
            }
        }
        initialCriteria {
            def user = springSecurityService.currentUser
            rentalList {
                manager {
                    eq ('id', user.customerID)
                }
            }
            eq('ord_type','LRO')
            order("orddate","desc")
        }
        enableFilter true
        inlineEdit false
        columns {
            id {
                value { order -> order.ordnum }
                jqgrid {
                    hidden true
                }
            }
            ordnum
            mgrnum
            bkrnum
            mailer
            orddate
            maildate
            wantdate
            shipdate 
            statusDesc
            decoy 
        }
    }

    def managerListOrderDetailsGrid = {
        dataSourceType 'gorm'
        domainClass Ordline
        globalFilterClosure{ params ->
            order {
                eq('ordnum', params.id ? params.id as long: -1l)
            }
        }
        jqgrid {
            sortname "linenum"
            sortorder "asc"
        }
        enableFilter false
        inlineEdit false
        columns {
            keycode
            segment
            ordqty
            shipqty
        }
    }

My data comes from legacy tables in an Oracle database. The id column for the Orders table is "ordnum", declared like this:

class Order {
    Long        ordnum
    static mapping = {
        table name: 'orders', schema:'sbowner'
        version false
        id column: 'ordnum', name: 'ordnum', generator: 'assigned'
}
   [other fields and mappings omitted]

Ordline (the subgrid data) looks like this:

class Ordline {
    Order   order
    Long    linenum
    static mapping = {
        table name: 'ordlines', schema:'sbowner'
        version false
        id composite: ['order', 'linenum' ], generator: 'assigned'
        order column: 'ord_ordnum'
}
  [other fields and mappings omitted]

I need to link the ordnum column in the Orders table with the ord_ordnum column in the Ordlines table. I wasn't sure how to do this, so I created a hidden column named "id" in the main grid and put the ordnum in as its value. I also set idColName 'ordnum', hoping that would link them up.

This hasn't worked. When I expand any subgrid row, it is empty. I can't quite figure out how to link everything up.

ericraskin commented 10 years ago

I've been digging through this code. I see a call to "easygrid.subGridRowExpanded" in your template, but I can't find that code anywhere. I'm stumped. :-)

tudor-malene commented 10 years ago

You can find that javascript function in easygrid\web-apps\js\jqgridutils\utils.js . This resource is declared in the easygrid resources module ,so it will be loaded all the time.

Here is how subgrids works:

ericraskin commented 10 years ago

Thanks. I will look for it. But I'm still not clear how the subgrid gets parameters passed in. How does I reference the values from the master row so I can load the proper rows for the subgrid?

On 04/10/2014 05:26 AM, Tudor Malene wrote:

You can find that javascript function in easygrid\web-apps\js\jqgridutils\utils.js . This resource is declared in the easygrid resources module ,so it will be loaded all the time.

Here is how subgrids works:

  • Basically all you need to do is declare the subgrid property on the main grid.
  • when you do this, the template will render that subgridRowExpanded function that will call the 'Html' action for that subgrid ( That action will actually return the javascript & html code for the subgrid, which in turn, when loaded, will call the Rows method ) . The drawback on this approach , (for now), is that you will have to set all the view properties for the subgrid in the controller.

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40058732.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

tudor-malene commented 10 years ago

In that javascript function the "row id" is passed and appended to the 'Html' url. Which in turn uses it to retreive the rows. So, basically, in the globalFilterClosure, the parent row id = params.id

ericraskin commented 10 years ago

I see. Unfortunately, my field is not named "id" in the master or detail rows. I wonder if that has anything to do with it. My id is "ordnum" in the master row and "ord_ordnum" (part of a composite key) in the detail row.

I will try to read the code, but I haven't found it yet. :-/ I'm using ggts on Linux and sometimes it behaves strangely.

On 04/10/2014 09:05 AM, Tudor Malene wrote:

In that javascript function the "row id" is passed and appended to the 'Html' url. Which in turn uses it to retreive the rows. So, basically, in the globalFilterClosure, the parent row id = params.id

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40075043.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

tudor-malene commented 10 years ago

Each row must have an id, provided by the column with the name: 'idColName'. it's returned in the json response from the server

ericraskin commented 10 years ago

Yep. Thanks. I am tracking all of this through my Firebug. The funny thing is I can't find the easygrid javascript on the running pages. i can find the jqgrid definition and my javascript in the DOM, but I can't find your javascript anywhere.

I assume it is there somewhere because I'm not getting any errors in the console.

I had put a breakpoint on the grails side in the globalFilterClosure for the subgrid and it did not stop. I watched network transfers from the Firebug side when I clicked to open the subgrid and nothing transferred. I'm 99% sure that Firebug shows ajax transfers, but maybe I'm wrong.

The whole thing has me very confused, but I'll figure it out eventually.

On 04/10/2014 11:11 AM, Tudor Malene wrote:

Each row must have an id, provided by the column with the name: 'idColName'. it's returned in the json response from the server

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40097356.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

tudor-malene commented 10 years ago

That javascript file is bundled together with jqgrid ( the resources plugin does this ).

Regarding the subgrid, does it open when you expand a row? If not, do you see any javascript error?

ericraskin commented 10 years ago

It opens, but the display is empty. No headers, no rows.

On 04/10/2014 11:44 AM, Tudor Malene wrote:

That javascript file is bundled together with jqgrid ( the resources plugin does this ).

Regarding the subgrid, does it open when you expand a row? If not, do you see any javascript error?

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40101489.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

tudor-malene commented 10 years ago

any errors?

ericraskin commented 10 years ago

Not that I can find. That's what is so strange. It just opens up an empty row on the grid.

On 04/10/2014 11:47 AM, Tudor Malene wrote:

any errors?

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40101947.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

tudor-malene commented 10 years ago

what ajax calls do you see? Can you also try this in chrome?

ericraskin commented 10 years ago

None -- that's part of the problem. I see nothing under the Network tab when I click the subgrid link. That's what makes me think the javascript isn't getting loaded into the page or something. I thought I would see an error about a missing function, but I'm not seeing that either.

It is very strange.

On 04/10/2014 11:52 AM, Tudor Malene wrote:

what ajax calls do you see? Can you also try this in chrome?

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40102486.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

tudor-malene commented 10 years ago

Can you send me the source of the page? ( 'view source')

ericraskin commented 10 years ago

Sure, before or after expanding the subgrid?

On 04/10/2014 11:57 AM, Tudor Malene wrote:

Can you send me the source of the page? ( 'view source')

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40103539.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

tudor-malene commented 10 years ago

the source is the same in both cases. The subgrid code is injected dynamically in the DOM

ericraskin commented 10 years ago

OK.

On 04/10/2014 12:03 PM, Tudor Malene wrote:

the source is the same in both cases. The subgrid code is injected dynamically in the DOM

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40104212.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

ericraskin commented 10 years ago
<!DOCTYPE html>
<html lang="en">
<!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
<!--[if IE 7 ]>    <html lang="en" class="no-js ie7"> <![endif]-->
<!--[if IE 8 ]>    <html lang="en" class="no-js ie8"> <![endif]-->
<!--[if IE 9 ]>    <html lang="en" class="no-js ie9"> <![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"><!--<![endif]-->
<head>
    <meta charset="utf-8">
    <title>Professional Advertising Systems Inc.</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <meta name="author" content="">
    <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
    <!--[if lt IE 9]>
    <script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
    <!-- Latest compiled and minified CSS -->
    <link href="//netdna.bootstrapcdn.com/bootswatch/3.1.1/cyborg/bootstrap.min.css" rel="stylesheet" type="text/css" media="screen" >
    <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
    <link href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/start/jquery-ui.min.css" rel="stylesheet">
    <link href="/pasweb/static/css/base.black.css" rel="stylesheet" type="text/css">
    <!--link href="//netdna.bootstrapcdn.com/bootswatch/3.1.1/cerulean/bootstrap.min.css" rel="stylesheet" type="text/css" media="screen" -->
    <!--link href="/pasweb/static/css/base.white.css" rel="stylesheet" type="text/css"-->
    <style type="text/css" id="enject"></style>

</head>
<body>
    <section id="headerSection">
        <div class="navbar navbar-default" role="navigation">
          <div class="container-fluid">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="index">Professional Advertising Systems <small> Inc.</small></a>
            </div>
            <div class="collapse navbar-collapse" id="navbar-collapse">
                <ul class="nav navbar-nav navbar-right">
                    <li class=""><a href="/pasweb/">Home</a></li>
                    <li class=""><a href="/pasweb/services">What We Do</a></li>  
                    <li class=""><a href="/pasweb/about_us">About Us</a></li>
                    <!-- li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">Features<b class="caret"></b></a>
                        <ul class="dropdown-menu">
                            <li><a href="elements">Page Elements</a></li>
                            <li><a href="comingsoon">Coming soon page</a></li>
                        </ul>
                    </li-->
                    <li class=""><a href="/pasweb/customeraccess">Customer Access</a></li>
                    <li class=""><a href="/pasweb/contact">Contact Us</a></li>
                </ul>
            </div>
          </div>
        </div>
    </section>
    <!--Header Ends================================================ -->

    <section id="bannerSection">
        <div class="container"> 
          <div class="col-md-4">
            <h1 id="pageTitle">Customer Access</h1>
          </div>
          <div class="col-md-5"></div>
          <div class="col-md-3" style="position:relative; top:30px;">

               Logged in as <strong>saavoy</strong> <a href="/pasweb/logout/index" class="btn btn-primary">Logout</a>

          </div>
        </div>
    </section> 

    <section id="bodySection">
      <div class="container-fluid" style="padding-bottom: 10px;">
          <div class="col-md-2">
            <ul class="nav nav-stacked">

                <li><a href="/pasweb/customerAccess/broker_mailers" onclick="jQuery.ajax({type:'POST', url:'/pasweb/customerAccess/broker_mailers',success:function(data,textStatus){jQuery('#mainContent').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){}});return false;" class="btn btn-primary">Mailers/Merges</a></li>

                <li><a href="/pasweb/customerAccess/manager_lists" onclick="jQuery.ajax({type:'POST', url:'/pasweb/customerAccess/manager_lists',success:function(data,textStatus){jQuery('#mainContent').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){}});return false;" class="btn btn-primary">Lists/Orders</a></li>

            </ul>
          </div>
          <div class="col-md-10">
            <div id="mainContent">
            </div>
          </div>
        </div>
    </section>

    <!-- Footer Start ============================================= -->
    <section id="footerSection">
        <div class="container">
            <footer class="footer well well-sm">
              <div class="row text-center"> 
                <div class="col-md-4">
                    <h4>Visit us</h4>
                    <address style="margin-bottom:15px;margin-right:-30px">
                    <strong><a href="index" title="business"><i class="fa fa-home"></i> Professional Advertising Systems Inc. </a></strong><br>
                        200 Business Park Drive, Suite 304<br>
                        Armonk, NY 10504<br>
                    </address>
                </div>
                <div class="col-md-4">
                    <h4>Contact Us</h4>
                    Phone: <i class="icon-phone-sign"></i> &nbsp; 914-765-0500 <br>
                    Email: <a href="contact" title="contact"><i class="icon-envelope-alt"></i> info@paslists.com</a><br/>
                    Link: <a href="index" title="Professional Advertising Systems Inc."><i class="fa fa-globe"></i> www.paslists.com</a><br/><br/>
                </div>
                <div class="col-md-4">
                    <h4>Quick Links</h4>    
                    <a href="services" title="services"><i class="fa fa-cogs"></i> Services </a><br/>
                    <a href="about" title=""><i class="fa fa-info"></i> About Us </a><br/>
                    <a href="contact" title="Contact Us"><i class="fa fa-question"></i> Contact Us </a>
                </div> 
            </div>      
            <p style="padding:18px 0 44px">&copy; 2014 Professional Advertising Systems Inc., All Rights Reserved </p>
            </footer>
        </div><!-- /container -->
    </section>
    <a href="#" class="btn" style="position: fixed; bottom: 38px; right: 10px; display: none; " id="toTop"> <i class="icon-arrow-up"></i> Go to top</a>
    <!-- Javascript
        ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
        <script src="//platform.twitter.com/widgets.js"></script>
        <script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
        <script src="//code.jquery.com/jquery-migrate-1.2.1.js"></script>
        <!-- Latest compiled and minified JavaScript -->
        <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
        <script src="/pasweb/static/js/business_ltd_1.0.js"></script>

        <script>
            /*menu handler*/
            $(function(){
              function stripTrailingSlash(str) {
                if(str.substr(-1) == '/') {
                  return str.substr(0, str.length - 1);
                }
                return str;
              }

              console.log('------------------------');
              var url = window.location.pathname;  
              console.log('url = ' + url);

              var activePage = stripTrailingSlash(url);
              console.log('activePage = ' + activePage);

              if (activePage == '/pasweb') {
                activePage = '/pasweb/home';
              }
              if (activePage == '/pasweb/login/auth') {
                activePage = '/pasweb/customeraccess';
              }
              console.log('activePage = ' + activePage);

              $('.nav li a').each(function(){  
                var currentPage = stripTrailingSlash($(this).attr('href'));
                if (currentPage == '/pasweb') {
                    currentPage = '/pasweb/home';
                }
                console.log('currentPage = ' + currentPage);

                if (activePage == currentPage) {
                  console.log('setting ' + currentPage + 'active');
                  $(this).parent().addClass('active'); 
                  console.log('set ACTIVE');
                }
              });
              console.log('------------------------');
            });
      </script>
      <script>
      $(document).ready(function() {
            $('form:first *:input[type!=hidden]:first').focus();
        });
      </script>

    </body>
</html>
tudor-malene commented 10 years ago

I don't see any grid definition

ericraskin commented 10 years ago

Wait. I gave you bad information. I did see Network traffic when I clicked the link. URL is: https://localhost:8443/pasweb/customerAccess/managerListOrderDetailsHtml?id=30521&gridId=row30521

That does have the ID for one of my orders (30521). So the URL is being built correctly. Here is the reply:

<table id="row30521_table"></table>

<div id="row30521Pager"></div>

<script type="text/javascript">jQuery(function(){
    jQuery("#row30521_table").jqGrid({
    url:
'/pasweb/customerAccess/managerListOrderDetailsRows/30521?gridId=row30521',
    loadError: easygrid.loadError,
    pager: '#row30521Pager',

"datatype":"json","viewrecords":true,"width":"100%","height":240,"rowNum":10,"rowList":[10,20,50],"multiSort":true,

    colModel: [

{"label":"Keycode","searchoptions":{"clearSearch":false,"sopt":["cn","nc","eq","ne","bw","ew"]},"search":true,"editable":true,"sortable":true,"name":"keycode","width":"100"

            },

{"label":"Segment","searchoptions":{"clearSearch":false,"sopt":["cn","nc","eq","ne","bw","ew"]},"search":true,"editable":true,"sortable":true,"name":"segment","width":"150"

            },

            {"label":"Order
Qty","searchoptions":{"clearSearch":false,"sopt":["eq","ne","lt","le","gt","ge"]},"search":true,"editable":true,"sortable":true,"name":"ordqty","width":"100"

            },

            {"label":"Ship
Qty","searchoptions":{"clearSearch":false,"sopt":["eq","ne","lt","le","gt","ge"]},"search":true,"editable":true,"sortable":true,"name":"shipqty","width":"100"

            },

    ],

    });

        jQuery('#row30521_table').jqGrid('navGrid','#row30521Pager',

{"add":false,"view":true,"edit":false,"del":false,"search":true,"refresh":true},
        {},     //edit
        {},     //add
        {},     //delete

{"multipleSearch":true,"multipleGroup":true,"showQuery":true,"caption":"Multi-clause
Searching","closeAfterSearch":true,"sopt":["eq","ne","lt","le","gt","ge","bw","bn","ew","en","cn","nc","nu","nn"]},    
//search
        {"closeOnEscape":true}     //view
        )

}); </script>

That is the correct subgrid, so something is coming back. I guess the problem is somewhere in my globalFilterCriteria. Where should I set a breakpoint to debug it? I have one set inside the globalFilterCriteria itself, but the debugger doesn't stop there.

On 04/10/2014 12:03 PM, Tudor Malene wrote:

the source is the same in both cases. The subgrid code is injected dynamically in the DOM

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40104212.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

ericraskin commented 10 years ago

Yep -- it is entirely inside the DOM. It is not in the page source. I use ajax calls to bring up the correct page from my buttons.

Could that be the problem? What draws in the javascript resources when ajax calls are made?

On 04/10/2014 12:10 PM, Tudor Malene wrote:

I don't see any grid definition

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40105008.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

tudor-malene commented 10 years ago

The browser doesn't really care how the DOM is populated.

Your code looks good to me, and if you don't see any javascript errors, then it should be ok.

I would try 3 things: 1) Do a manual call to: /pasweb/customerAccess/managerListOrderDetailsRows/30521?gridId=row30521

And please post back what it returns ( also do a call to: /pasweb/customerAccess/managerListOrderDetailsRows) This is a general good way to debug

2) change the browser to chrome, and see if you see any errors in the javascript console there 3) comment out the globalFilterClosure

ericraskin commented 10 years ago

I think I have found the issue. It is the composite id. I would have to add more code to the domain class to handle it. Even then, I'm not sure how the lookup code will work, as the parameter being passed is only the first part of the composite id. I'm not quite clear how the system will deal with one part of the primary key being null. We want it to load all the records that match, but I think gorm will look for a record with that particular key (second part of id null).

I may just add a surrogate primary key and avoid the whole issue. It should not be too hard to add to my legacy db.

I will post more once I figure this out. On Apr 11, 2014 2:48 AM, "Tudor Malene" notifications@github.com wrote:

The browser doesn't really care how the DOM is populated.

Your code looks good to me, and if you don't see any javascript errors, then it should be ok.

I would try 3 things: 1) Do a manual call to: /pasweb/customerAccess/managerListOrderDetailsRows/30521?gridId=row30521

And please post back what it returns ( also do a call to: /pasweb/customerAccess/managerListOrderDetailsRows) This is a general good way to debug

2) change the browser to chrome, and see if you see any errors in the javascript console there 3) comment out the globalFilterClosure

— Reply to this email directly or view it on GitHubhttps://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40175251 .

ericraskin commented 10 years ago

First, I modified my setup to get rid of the composite ID. I guess I still did not do it correctly or there is some other problem, because it still did not work.

When I tried to do a manual call, I got this error:

Error 2014-04-11 17:20:36,422 [http-bio-8443-exec-9] ERROR errors.GrailsExceptionResolver - NumberFormatException occurred when processing request: [GET] /pasweb/customerAccess/managerListOrderDetailsRows/30521%3FgridId=row30521 For input string: "30521?gridId=row30521". Stacktrace follows: Message: For input string: "30521?gridId=row30521" Line | Method ->> 65 | forInputString in java.lang.NumberFormatException


| 441 | parseLong in java.lang.Long | 540 | valueOf . . . . . . . . . . . . . . . in '' | 3637 | toLong in org.codehaus.groovy.runtime.StringGroovyMethods

I truncated the stack trace. It seems that more than just the id of the master row (30521) is getting sent as the params.id value. At least, that's what it looks like to me?

Commenting out the globalFilterClosure returns and executing the manual call returns JSON data:

{"rows":[{"id":null,"cell":[null,"Entire File for NCOA",45000,36396]},{"id":null,"cell":[null,"1999 Buyers",5000,4999]},{"id":null,"cell":[null,"0-24 $10+ w/State Select",12500,10767]},{"id":null,"cell":[null,"Active Donors w/SCF Select",6000,5967]},{"id":null,"cell":["N164","0-12 $10+ Donors",30000,29994]},{"id":null,"cell":["HCU136","1999 Buyers",10000,9989]},{"id":null,"cell":["HCU142","1999 Buyers",25000,24994]},{"id":null,"cell":[null,"0-12 $10+ Donors",25000,24989]},{"id":null,"cell":[null,"0-6 $10+ Donors",50000,45383]},{"id":null,"cell":[null,"2nd Qtr 2000 Hotline w/State Omits",5000,4959]},{"id":null,"cell":[null,"0-12 $5+ Donors",100000,100003]},{"id":null,"cell":["7 AO L/J","July 2000 Hotline Buyers",9000,10273]},{"id":null,"cell":["4590","July 2000 HL with State Sels",5000,5002]},{"id":null,"cell":["J14901","July 2000 Julia",1173,1250]},{"id":null,"cell":["ILIS08T1","July 2000 HL w/State Omits",5000,5002]},{"id":null,"cell":["914","Last 12 Mos $10+ Donors",20000,20003]},{"id":null,"cell":[null,"June 2000 Hotline",5000,2986]},{"id":null,"cell":["IDA10G","0-12 $5+ Donors Omit States/SCF",15000,15003]},{"id":null,"cell":[null,"0-24 $5+ Donors",10000,9474]},{"id":null,"cell":[null,"0-24 Mos $5+",10000,9474]}],"page":1,"records":70590,"total":3530}

So, something is clearly wrong with the globalFilterClosure. My latest incarnation is:

        globalFilterClosure{ params ->
            eq('order', params.id ? params.id as long: -1l)
        }

The ordline domain contains (abbreviated):

    Long    ordline_id
    Order    order

    static mapping = {
        table name: 'ordlines', schema:'sbowner'
        version false
        id column: 'ordline_id', name: 'ordline_id', generator: 'assigned'
        order column: 'ord_ordnum'
   }

What should I try now?

On 04/11/2014 02:48 AM, Tudor Malene wrote:

The browser doesn't really care how the DOM is populated.

Your code looks good to me, and if you don't see any javascript errors, then it should be ok.

I would try 3 things: 1) Do a manual call to: /pasweb/customerAccess/managerListOrderDetailsRows/30521?gridId=row30521

And please post back what it returns ( also do a call to: /pasweb/customerAccess/managerListOrderDetailsRows) This is a general good way to debug

2) change the browser to chrome, and see if you see any errors in the javascript console there 3) comment out the globalFilterClosure

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40175251.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

tudor-malene commented 10 years ago

Try this:

        globalFilterClosure{ params ->
            eq('order', params.long('id', -1l))
        }
ericraskin commented 10 years ago

Unfortunately, no change. :-(

On 04/14/2014 02:52 AM, Tudor Malene wrote:

Try this:

globalFilterClosure{ params -> eq('order', params.long('id', -1l)) }

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40337504.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

tudor-malene commented 10 years ago

You mean, you get the exception?

ericraskin commented 10 years ago

No -- no exception. When I use your new example globalFilterClosure and I go to URL: https://localhost:8443/pasweb/customerAccess/managerListOrderDetailsHtml?id=30521&gridId=row30521

I get nothing returned. I still don't have the access from Orders to Ordlines correct.

Where can I set a breakpoint in your code to watch the ajax json response being built on the server for the subgrid? Maybe if I can stop there I can see what is wrong with the globalFilterClosure.

ericraskin commented 10 years ago

So, this is definitely not an Easygrid issue right now. I opened the console and did this:

import pasweb.Order
import pasweb.Ordline

def order = Order.withCriteria{
    eq('ordnum',30810l)
}

println order

def ordlines = Ordline.withCriteria{
    eq('order',30810l)
}

The println works fine, but I get the following error on the Ordlines lookup:

2014-04-15 14:19:50,137 [Thread-19] ERROR property.BasicPropertyAccessor  - IllegalArgumentException in class: pasweb.Order, getter method of property: ordnum
Exception thrown

So, clearly something is not happy with the "long" type somewhere. I will have to research this at the hibernate level, I think. Open to suggestions if you have them.

tudor-malene commented 10 years ago

Looks to me like you should do this:

def ordlines = Ordline.withCriteria{
   order{
     eq('ordnum',30810l)
   }
}
ericraskin commented 10 years ago

Actually, that is the first thing I tried. I get this:

Order = [30810]
Exception thrown

groovy.lang.MissingMethodException: No signature of method:
java.util.ArrayList.call() is applicable for argument types:
(ConsoleScript20$_run_closure2_closure4) values:
[ConsoleScript20$_run_closure2_closure4@72eea7f5]
Possible solutions: tail(), wait(), last(), last(), max(), any()
    at ConsoleScript20$_run_closure2.doCall(ConsoleScript20:11)
    at ConsoleScript20$_run_closure2.doCall(ConsoleScript20)
    at
org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1260)
    at
grails.orm.HibernateCriteriaBuilder.invokeClosureNode(HibernateCriteriaBuilder.java:1854)
    at
grails.orm.HibernateCriteriaBuilder.invokeMethod(HibernateCriteriaBuilder.java:1553)
    at
org.grails.datastore.gorm.GormStaticApi$_withCriteria_closure11.doCall(GormStaticApi.groovy:305)
    at
org.springsource.loaded.ri.ReflectiveInterceptor.jlrMethodInvoke(ReflectiveInterceptor.java:1260)
    at com.sun.proxy.$Proxy39.doInSession(Unknown Source)
    at
org.grails.datastore.mapping.core.DatastoreUtils.execute(DatastoreUtils.java:302)
    at
org.grails.datastore.gorm.AbstractDatastoreApi.execute(AbstractDatastoreApi.groovy:37)
    at
org.grails.datastore.gorm.GormStaticApi.withCriteria(GormStaticApi.groovy:304)
    at pasweb.Ordline.withCriteria(Ordline.groovy)
    at pasweb.Ordline$withCriteria.call(Unknown Source)
    at ConsoleScript20.run(ConsoleScript20:10)

On 04/16/2014 02:48 AM, Tudor Malene wrote:

Looks to me like you should do this:

def ordlines = Ordline.withCriteria{ order{ eq('ordnum',30810l) } }

— Reply to this email directly or view it on GitHub https://github.com/tudor-malene/Easygrid/issues/65#issuecomment-40568247.


Eric H. Raskin 914-765-0500 x120 Professional Advertising Systems Inc. 914-765-0503 fax 200 Business Park Dr Suite 304 eraskin@paslists.com Armonk, NY 10504 http://www.paslists.com

tudor-malene commented 10 years ago

This looks to me like a bug in grails. You should create a small test project and file a bug in http://jira.grails.org/

ericraskin commented 10 years ago

I am working on creating the test case. I'm beginning to think that everything I "knew" about GORM is wrong. :-( I can't even get a simple association to work properly. I broke the test case into:

class TestOrder {

    Long        ordnum
    String        ord_type

    static hasMany = [ orderDetail: TestOrdline ]

    static mapping = {
        id column: 'ordnum', name: 'ordnum', generator: 'assigned'
    }

    static constraints = {
    }
}

class TestOrdline {

    Long        ordline_id
    Long         linenum

    static belongsTo = [order:TestOrder]

    static mapping = {
        id column: 'ordline_id', name: 'ordline_id', generator: 'assigned'
    }

    static constraints = {
    }

 }

Here's my test (using JUnit):

class Show_errorTest extends GroovyTestCase {

    def testOrder1
    def testOrder2

    def setup() {
        testOrder1 = new TestOrder(ordnum: 12345l, ord_type: 'LRO')
        testOrder1.save(flush:true)

        def testOrdline11 = new TestOrdline(order: testOrder1,
                                           linenum: 1, ordline_id: 100)
        testOrdline11.save(flush:true)

        def testOrdline12 = new TestOrdline(order: testOrder1, 
                                            linenum: 2, ordline_id: 101)
        testOrdline12.save(flush:true)

        testOrder2 = new TestOrder(ordnum: 54321l, ord_type: 'LRO')
        testOrder2.save(flush:true)

        def testOrdline21 = new TestOrdline(order: testOrder2, 
                                            linenum: 1, ordline_id: 102)
        testOrdline21.save(flush:true)
    }

    def cleanup() {
        testOrder1.delete
        testOrder2.delete
    }

    void testError() {

        setup()

        def orderList = TestOrder.withCriteria{
            eq('ordnum',12345l)
        }

        def order = orderList[0]

        println "order loaded"
        assert order

        assertEquals 12345, order.ordnum

        println "order: Order=${order.ordnum}"

        assertEquals 2, order.orderDetail.size()
        println "orderDetail: Count=${order.orderDetail.size()}"

        order.orderDetail.each {
            println "orderDetail: Order=${it.order.ordnum} 
Line=${it.linenum}  id=${it.ordline_id}"
        }

        cleanup()

    }
}

When I run this, the "assertEquals 2, order.orderDetail.size()" fails. When I run this in the debugger, I see that the orderDetail entry for the order object is null! It's an association! Why doesn't it have a proxy object? I'm accessing it! Why doesn't the proxy change to actual detail objects?

I am completely confused now.

Eric

tudor-malene commented 10 years ago

in your setup method , you should do: testOrder1.addToOrderDetails(testOrdline11 ) for each order line.

ericraskin commented 10 years ago

Yep -- that fixed the problem. I'm not quite sure why that was necessary, since I did link them together by providing the Order object when I created the OrderDetail object. I will just have to remember that this is necessary.

The funny thing is I posted this on the grails-user list and somebody came back with a (supposedly) working Spock test, using my setup() routine as written. They did not need the addToOrderDetail() call. :-/ I guess they didn't actually run the code they wrote.

I have now successfully created a test that shows the issue. It is posted as JIRA GRAILS-11336.

ericraskin commented 10 years ago

So, I found out what the bug in my test case is. I had used the same name for a local variable and a class. Groovy was taking the local variable instead of the class in the DSL.

def order=...

def testordline = Ordline.withCriteria {
   order {
        ....
   }
}

The "order" in my def was being used, instead of my association named "order" in Ordline.

I have renamed all of my fields in all of my domain classes so that no field name is the same as a class name. There should be no possible name collisions now.

Unfortunately, I still can't get the subgrid to show any data. However, now my test shows that the globalFilterCriteria should work. At least, it pulls up valid data in my tests. It does NOT work, however, when I use the ajax URL in the Firefox. Per your request in an earlier message, I have also tried it in Chrome. This URL:

/pasweb/customerAccess/managerListOrderDetailsHtml?id=38788&gridId=row38788

shows in the javascript console, but no data comes back.

Note that I upgraded to 1.5.1 this morning.

I still haven't found the place in the code where the ajax call for the subgrid is processed. I put a breakpoint inside GormDatasourceService.list(). The breakpoint gets hit on master and child grids, but not on subgrids. Where should I put a breakpoint -- or where is the plumbing that links the URL with the code so I can see how it fits together? There is a URL in the subgrid for the ajax call. How is that URL mapped to server code?

tudor-malene commented 10 years ago

This was one tricky bug. Good thing you sorted it out.

This is what learning grails the hard way means :)

To solve this subgrid problem faster, do you think it's possible that you create a very small test project just with those 2 domains and grids and submit it here, so I can debug it?

ericraskin commented 10 years ago

I tried to isolate it for you, but the test project worked. It just doesn't work in my real project.

Weirder yet, if I change the exact same grid from a subgrid to a child grid in my real project, it works fine. :-( It just fails as a subgrid. How can that be?

tudor-malene commented 10 years ago

This thread has grown pretty large. I'm a little confused : So, you have those 2 grids:

What happens when you open a row in the master grid? Exception? javascript error in the console? nothing? a table shows up but doesn't load the rows? What network calls are made by the browser?

If you remove the 'subGrid' property from the master grid, and use the 2 grids as master-detail grids ( wired via the taglibs), you said it worked properly?

ericraskin commented 10 years ago

If you like, we can close this and start a new thread.

Yes, that is correct. There are no javascript errors that I can find. No errors on the server side either. It just doesn't return any data.

When I remove the subGrid and add a masterGrid and childParamName in the view, it works fine with no other changes. Note, I'm showing you all three grids here. The grid with a subGrid is itself a child grid.

       <div class="row">
          <div class="col-md-10">
            <p>Click on a List to view the rental orders</p>
            <grid:grid name="managerList">
                <grid:set width="auto" height="auto" caption="Lists" />
                <grid:set col="counts" label="Counts" align="center" formatter="f:countsViewButton" />
            </grid:grid>
          </div>
      </div>
      <br/>
      <div class="row">
        <div class="col-md-10">
          <p>Click on an order to view the order details</p>
          <grid:grid name="managerListOrders" masterGrid="managerList" childParamName="listId">
              <grid:set width="auto" height="auto" caption="Rental Orders"/>
          </grid:grid>
          <grid:exportButton name="managerListOrders" formats="['excel','csv','pdf']"/>
        </div>
      </div>
      <!--div class="row">
        <div class="col-md-10">
          <grid:grid name="managerListOrderDetails" masterGrid="managerListOrders" childParamName="id" jqgrid.caption='"Rental Orders"'/>
          <grid:exportButton name="managerListOrderDetails" formats="['excel','csv','pdf']"/>
        </div>
      </div-->

When I go back to a grid with a subGrid, the subGrid does not display anything. It opens up on the jqgrid display, but it just shows a single blank line. There isn't even an "empty" subgrid displayed.

Could the fact that the grid with the subGrid is a child grid make a difference somehow? I will have to add that to my test.

tudor-malene commented 10 years ago

So, the same grid, is on the same page both a subgrid and a child grid?

ericraskin commented 10 years ago

Nope - one or the other. Mutually exclusive. I tried it as a subgrid and it didn't work. I then changed the definition (commented out the subgrid) and changed the view to display as a child grid. It worked.

tudor-malene commented 10 years ago

The main difference between the 2 is that, for the subgrid , the grid is added to the DOM from the html code generated by that Html call. But it's odd that you see no javascript error. Does the browser even make that call?

ericraskin commented 10 years ago

It appears to. When I look at network traffic, a call goes out. Nothing comes back

tudor-malene commented 10 years ago

could you run that call and check out the response? What is the exact call ?

ericraskin commented 10 years ago

https://localhost:8443/pasweb/customerAccess/managerListOrderDetailsHtml?id=38788&gridId=row38788 is the URL.

Response is:

<table id="row38788_table"></table>

<div id="row38788Pager"></div>

<script type="text/javascript">jQuery(function(){
    jQuery("#row38788_table").jqGrid({
    url: '/pasweb/customerAccess/managerListOrderDetailsRows/38788?gridId=row38788',
    loadError: easygrid.loadError,
    pager: '#row38788Pager',
    "datatype":"json","viewrecords":true,"width":"100%","height":240,"rowNum":10,"rowList":[10,20,50],"multiSort":true,

    colModel: [

            {"label":"Keycode","searchoptions":{"clearSearch":false},"search":false,"sortable":true,"name":"keycode","width":"100"

            },

            {"label":"Segment","searchoptions":{"clearSearch":false},"search":false,"sortable":true,"name":"segment","width":"150"

            },

            {"label":"Order Qty","searchoptions":{"clearSearch":false},"search":false,"sortable":true,"name":"ordqty","width":"100"

            },

            {"label":"Ship Qty","searchoptions":{"clearSearch":false},"search":false,"sortable":true,"name":"shipqty","width":"100"

            },

    ],

    });

        jQuery('#row38788_table').jqGrid('navGrid','#row38788Pager',
        {"add":false,"view":true,"edit":false,"del":false,"search":true,"refresh":true},
        {},     //edit
        {},     //add
        {},     //delete
        {"multipleSearch":true,"multipleGroup":true,"showQuery":true,"caption":"Multi-clause Searching","closeAfterSearch":true,"sopt":["eq","ne","lt","le","gt","ge","bw","bn","ew","en","cn","nc","nu","nn"]},     //search
        {"closeOnEscape":true}     //view
        )

}); </script>

Console shows:

POST https://localhost:8443/pasweb/j_spring_security_check [HTTP/1.1 302 Found 121ms]
GET https://localhost:8443/pasweb/customeraccess [HTTP/1.1 200 OK 309ms]
"JQMIGRATE: Logging is active" jquery-migrate-1.2.1.js:21
"------------------------" customeraccess:154
"url = /pasweb/customeraccess" customeraccess:156
"activePage = /pasweb/customeraccess" customeraccess:159
"activePage = /pasweb/customeraccess" customeraccess:167
"currentPage = /pasweb/home" customeraccess:174
"currentPage = /pasweb/services" customeraccess:174
"currentPage = /pasweb/about_us" customeraccess:174
"currentPage = /pasweb/customeraccess" customeraccess:174
"setting /pasweb/customeraccessactive" customeraccess:177
"set ACTIVE" customeraccess:179
"currentPage = /pasweb/contact" customeraccess:174
"currentPage = /pasweb/customerAccess/broker_mailers" customeraccess:174
"currentPage = /pasweb/customerAccess/manager_lists" customeraccess:174
"------------------------" customeraccess:182
POST https://localhost:8443/pasweb/customerAccess/manager_lists [HTTP/1.1 200 OK 961ms]
GET https://localhost:8443/pasweb/static/plugins/jquery-1.11.0/js/jquery/jquery-1.11.0.min.js [HTTP/1.1 200 OK 10ms]
GET https://localhost:8443/pasweb/static/bundle-bundle_easygrid-jqgrid-dev_head.js [HTTP/1.1 200 OK 7ms]
GET https://localhost:8443/pasweb/static/plugins/jquery-ui-1.8.24/jquery-ui/js/jquery-ui-1.8.24.custom.js [HTTP/1.1 200 OK 5ms]
TypeError: jQuery(...).jqGridMethod is not a function jquery-1.11.0.min.js:3
GET https://localhost:8443/pasweb/customerAccess/managerListRows [HTTP/1.1 200 OK 2374ms]
GET https://localhost:8443/pasweb/customerAccess/managerListOrdersRows [HTTP/1.1 200 OK 150ms]
GET https://localhost:8443/pasweb/customerAccess/managerListOrdersRows [HTTP/1.1 200 OK 360ms]
GET https://localhost:8443/pasweb/customerAccess/managerListOrderDetailsHtml [HTTP/1.1 200 OK 36ms]
"/pasweb/customerAccess/managerListOrderDetailsHtml?id=38788&gridId=row38788" jquery-1.11.0.min.js:13442
GET https://localhost:8443/pasweb/customerAccess/managerListOrderDetailsHtml [HTTP/1.1 200 OK 37ms]

Note that some of this is my own debugging info for other parts of the site.

ericraskin commented 10 years ago

Screen shot showing results:

image

As you can see, the subgrid opens up empty.

tudor-malene commented 10 years ago

Seems like we'll have to do some javascript debugging.

Go to: \js\jqgridutils\utils.js - in the easygrid plugin, and find the property: subGridRowExpanded There you will find a function:

    subGridRowExpanded: function (baseUrl) {
        return function (subgrid_id, row_id) {
            var url = baseUrl + "?id=" + row_id + "&gridId=row" + row_id;
            console.log(url);
            $.ajax({
                url: url,
                dataType: "html",
                success: function (data) {
                    jQuery("#" + subgrid_id).html(data);
                }
            });
        }
    },

This function is actually called when you expand the row. The problem seems to be with the success callback. You should add some console.log messages there to see what's going on:

                success: function (data) {
                     console.log('subgrid loaded');
                     console.log(data);
                    jQuery("#" + subgrid_id).html(data);
                     console.log('after loading');
                }

Let me know how that goes.

ericraskin commented 10 years ago

So, here is the output:

GET https://localhost:8443/pasweb/customerAccess/managerListOrderDetailsHtml [HTTP/1.1 200 OK 43ms]
"/pasweb/customerAccess/managerListOrderDetailsHtml?id=38788&gridId=row38788"        jquery-1.11.0.min.js:13442
"subgrid loaded"                                                                     jquery-1.11.0.min.js:13447
"

<table id="row38788_table"></table>

<div id="row38788Pager"></div>

<script type="text/javascript">jQuery(function(){

    jQuery("#row38788_table").jqGrid({

    url: '/pasweb/customerAccess/managerListOrderDetailsRows/38788?gridId=row38788',

    loadError: easygrid.loadError,

    pager: '#row38788Pager',

    "datatype":"json","viewrecords":true,"width":"100%","height":240,"rowNum":10,"rowList":[10,20,50],"multiSort":true,

    colModel: [

            {"label":"Keycode","searchoptions":{"clearSearch":false},"search":false,"sortable":true,"name":"keycode","width":"100"

            },

            {"label":"Segment","searchoptions":{"clearSearch":false},"search":false,"sortable":true,"name":"segment","width":"150"

            },

            {"label":"Order Qty","searchoptions":{"clearSearch":false},"search":false,"sortable":true,"name":"ordqty","width":"100"

            },

            {"label":"Ship Qty","searchoptions":{"clearSearch":false},"search":false,"sortable":true,"name":"shipqty","width":"100"

            },

    ],

    });

        jQuery('#row38788_table').jqGrid('navGrid','#row38788Pager',

        {"add":false,"view":true,"edit":false,"del":false,"search":true,"refresh":true},

        {},     //edit

        {},     //add

        {},     //delete

        {"multipleSearch":true,"multipleGroup":true,"showQuery":true,"caption":"Multi-clause Searching","closeAfterSearch":true,"sopt":["eq","ne","lt","le","gt","ge","bw","bn","ew","en","cn","nc","nu","nn"]},     //search

        {"closeOnEscape":true}     //view

        )

}); </script>

" jquery-1.11.0.min.js:13448
"after loading"

Looks just like what I posted before, so that is what is really coming back from the server.

ericraskin commented 10 years ago

I put more of my regular site into the test and it still worked. I have ruled out the child grid status as being the issue.