title: How to build a flexible jqGrid with multiple options for data load?
body: <p>I am working on a jqGrid that will be used to view users. I'm trying to make this grid as flexible and robust as possible as it may be included in several places - it will be used as a standalone page to view/search through all users, it will be included on a specific company's page filtered by that company to show all the users within that company, and it will be used as a result of a "quick search" available on the site (input a single search term, hit "quick search", and it will pull up any user with a meaningful field that matches the search term). So I need to accommodate 3 options:</p>
  
  <ol>
  <li>Nothing passed in - load an empty grid with the ability to filter on specific columns (I'm using the filterToolbar)</li>
  <li>Quick Search Term passed in - invoke the search with that term</li>
  <li>Filters passed in (in a single URL parameter in JSON format) - invoke the search with those column filters (and load those filters into the toolbar)</li>
  </ol>
  
  <p>I have a ColdFusion cfc function that accepts the jqGrid params (page, sidx, etc), the column filter params, and the quick search param. If the quick search is passed in it invokes a quick search stored procedure; if it doesn't it invokes a separate filter stored procedure passing the column filters. In either case it returns the current page's results in the proper format for the grid.</p>
  
  <p>I am having a hard time navigating the jqGrid docs to figure out the best way to accomplish this. I'm not sure how to initially load the grid up with no results, or how to pass in the filters or the quick search term from the URL <em>before</em> loading the grid so we can pull up a predefined search. I am able to accomplish it with the following logic:</p>
  
  <ul>
  <li>Determine whether quickSearch was passed in. If so, set the URL to myFunctionURL?quickSearch=passedinSearchTerm</li>
  <li>If quickSearch was not passed in, set URL to myFunctionURL?initialLoad=true (and set up the function to return no results if initialLoad is true)</li>
  <li>On <code>loadComplete</code>, (if quickSearch was not passed in and this is the initialLoad) load all the filters into the filter toolbar, reset the URL programmatically to plain myFunctionURL, and invoke <code>triggerToolbar</code></li>
  </ul>
  
  <p>This seems really hacky. What is a cleaner way to accomplish this workflow?</p>
  
  <p>EDIT:
  Here's some sample code. Keep in mind that some of the logic is in pseudocode so may not be syntactically correct. The grid looks something like this:</p>
  
  <pre><code>$(function(){
      var initialLoad = true;
      var quickSearch = getUrlParam("quickSearch");
      var basicGridURL = 'myCfc.cfc?method=myFunction';
      var gridURL = basicGridURL;
      if(len(quickSearch)){
          gridURL = gridURL + '&amp;quickSearch=' + quickSearch;
      } else{
          gridURL = gridURL + '&amp;initialLoad=' + initialLoad;
      }
      $("#users").jqGrid({
              datatype: 'json',
              url: gridURL,
              gridview: true,
              colModel: [
                  {name: 'lastname', label: 'Last Name'},
                  {name: 'firstname', label: 'First Name'},
                  ... more column code here ...
                ],
              height:'auto',
              autowidth:true,
              caption:'Users',
              rowNum:20,
              rowList:[10,20,50],
              sortorder:'asc',
              sortname: 'lastname',
              ignoreCase: true, // case-insensitive filtering
              pager: '#pager',
              jsonReader: {
                  root: "ROWS", //our data
                  page: "PAGE", //current page
                  total: "TOTAL", //total pages
                  records:"RECORDS", //total records
                  cell: "", //not used
                  id: "0" //will default first column as ID
              },
              // after loading, fill the filter toolbar with any filters passed in through the URL and trigger search
              loadComplete: function(){
                  // load filterParams from URL and cpnvert to object
                  filterParams = $.parseJSON(getUrlParam(filterParams));
                  // if this is the first load of the table and there were filterParams passed in
                  if(initialLoad and filterParams.length){
                      // it seems to need a timeout to allow everything to load properly before triggering
                      setTimeout(function () {
                          // loop through filter toolbar elements and set any passed-in filters. Example for a single element:
                          $('#gs_lastname').val(filterParams.lastName);
                          // set URL to basic function without any extra params
                          $("#users").jqGrid('setGridParam',{url:basicGridURL});
                          // trigger search
                          $('#users')[0].triggerToolbar();
                      }, 100);
                      initialLoad = false;
                  }
              }
          });
      $("#users").jqGrid("filterToolbar", {searchOnEnter: true});
  
  });
  &lt;/script&gt;
  
  &lt;table id="users"&gt;&lt;tr&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
  &lt;div id="pager"&gt;&lt;/div&gt;
  </code></pre>
  
  <p>The cfc function looks something like this:
  component {</p>
  
  <pre><code>/* Arguments passed in by jqGrid:
      *  1. page - page user is on
      *  2. rows - number of rows to display per page
      *  3. sidx - sort column
      *  4. sord - sort order */
      remote String function getUsersForGrid(numeric page, numeric rows, String sidx, String sord, initialLoad="false",
                                          String firstName='', String lastName='' ... additional filter params here ...
                                          String quickSearchTerm) returnformat="JSON" {
          // if this is the first load, return no data
          if(initialLoad){
              return SerializeJSON({total=0, page=0, records=0, rows=[]});
          }
  
          // if quickSearchTerm is passed in, run quickSearch; otherwise run regular filtered search
          if(isDefined("arguments.quickSearchTerm")){
              users = ... invoke stored proc here ...
          } else{
              //  invoke stored proc with filters - get all users that match this search
              users = ... invoke stored proc here ...
          }
  
          // sanitize the ordering inputs (if it doesn't match a valid value it will throw exception)
          param name="sidx" type="regex" pattern="(?i)^(?:firstName|lastName|companyname|cabgroupid|status|email|state|username)$";
          param name="sord" type="regex" pattern="(?i)^(?:asc|desc)$";
  
          // qoq to order by dynamic variables
          users = new Query(
              dbType="query",
              users = users,
              sql = "SELECT * FROM users ORDER BY " &amp; sidx &amp; " " &amp; sord
          ).execute().getResult();
  
          // array to hold all users in current page
          var arrUsers = ArrayNew(1);
          // calculate start row
          var start = ((arguments.page-1) * arguments.rows) + 1;
          // calculate end row
          var end = (start - 1) + arguments.rows;
          end = (end &gt; users.recordCount) ? users.recordCount : end;
          var totalPages = Ceiling(users.recordcount / arguments.rows);
  
          for(currRow = start; currRow &lt;= end; currRow++){
              currUser = [users["lastname"][currRow],
                          users["firstname"][currRow],
                          ... additional columns here ...;
              ArrayAppend(arrUsers, currUser);
          }
  
          return SerializeJSON({total=totalPages, page=arguments.page, records=users.recordcount, rows=arrUsers});
      }
  }
  </code></pre>