0

In a related question, I asked how to filter down to and auto-select a given row in a jqGrid. The solution I currently have for this is:

$(function () {
   $('.relatedrecipe').click(function () {
      // store off the value of the related recipe I want to switch to
      var recipe = $(this).data('recipename');
      // clear any filters on the grid
      setTimeout(function () {
         $("#recipegrid")[0].clearToolbar();
      }, 50);
      // set the recipe filter to the related recipe name and trigger the filtering
      setTimeout(function () {
         $('#gs_RecipeName').val(recipe);
         $('#recipegrid')[0].triggerToolbar();
      }, 200);
      // auto-select the first row
      setTimeout(function () {
         var firstRowID = $('#recipegrid').jqGrid('getDataIds')[0];
         $('#recipegrid').setSelection(firstRowId, true);      
      }, 500);
   });
}

What I don't like about this solution (I do like that it provided me with a solution) is that I am, essentially, queuing up a bunch of functions to be run in the future, at 50ms, 200ms, and 500ms. This seems like a potentially problematic solution based on getting the timing right, so I don't like it much.

I've considered nesting these functions one inside another, with a 50ms time for each. Something like:

$(function () {
   $('.relatedrecipe').click(function () {
      // store off the value of the related recipe I want to switch to
      var recipe = $(this).data('recipename');
      // clear any filters on the grid
      setTimeout(function () {
         $("#recipegrid")[0].clearToolbar();
         // set the recipe filter to the related recipe name and trigger the filtering
         setTimeout(function () {
            $('#gs_RecipeName').val(recipe);
            $('#recipegrid')[0].triggerToolbar();
            // auto-select the first row
            setTimeout(function () {
               var firstRowID = $('#recipegrid').jqGrid('getDataIds')[0];
               $('#recipegrid').setSelection(firstRowId, true);      
            }, 50);
         }, 50);
      }, 50);
   });
}

Is that better? I've modified my code to do it this way and it seems to work as well, but is there a better way to do this?

The steps need to occur in this order but I believe there needs to be some time for each of the the first two sections to finish before doing the third. Any thoughts on this?

Community
  • 1
  • 1
itsmatt
  • 31,265
  • 10
  • 100
  • 164
  • Can you explain why you have to use `setTimeout()` at all? Are these not things that can just be normal sequential function calls? – jfriend00 Sep 16 '11 at 15:05

3 Answers3

1

How about something like this?

var exec_stack = [];
exec_stack.push(function() { 
    // do stuff 1
});
exec_stack.push(function() { 
    // do stuff 2
});
exec_stack.push(function() { 
    // do stuff 3
});

function run_stack(delay) {
    if (exec_stack.length > 0) {
        exec_stack.pop()();
        setTimeout(function() { run_stack(delay) }, delay);
    }
}
run_stack(50);

http://jsfiddle.net/SJmcG/

Naturally your functions don't have to be anonymous:

function foo() {
    //do stuff
}
exec_stack.push(foo);
Adam Terlson
  • 12,610
  • 4
  • 42
  • 63
0

My ultimate answer would depend on answering why things have to happen in that order.

It looks like a lot gates on the toolbar activity: if that's the case, it might be a good idea to tweak the toolbar to either (a) provide some callbacks, or (b) fire some custom events you can bind to.

As you surmise, relying on timing is a Pretty Bad Idea, especially if you want consistent behavior across browsers and across browser load.

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
0

I would suggest to rewrite your click handler as following:

$('.relatedrecipe').click(function () {
    // store off the value of the related recipe I want to switch to
    var recipe = $(this).data('recipename'),
        grid = $grid[0];

    // clear any filters on the grid WITHOUT reloading
    grid.clearToolbar(false);

    // set the recipe filter to the related recipe name
    $('#gs_RecipeName').val(recipe).trigger('change');

   // trigger the filtering
   grid.triggerToolbar();
});

It can be that in the code above the call of .trigger('change') is unneeded and you can remove it. Just test this.

The code which select the first row of the grid I would place inside of the loadComplete event handler. The callback function loadComplete will be called after the grid will be filled with the data.

loadComplete: function () {
    var $grid = $(this),
        $firstRowWithData = $grid.find("tr.jqgrow:first");

    if ($firstRowWithData.length > 0) {
        // if at least one row with data filled in the grid
        $grid.jqGrid ('setSelection', $firstRowWithData[0].id, true);
    }
}

The usage of getDataIds means that the jqGrid will go over all grid rows and fill the array with ids. Then you will get only the first one element from the array.

Another even more effective way to get the id of the first row with data will be in your case just

loadComplete: function () {
    var $firstRowWithData = this.rows;

    if (this.rows.length > 1) {
        // if at least one row with data filled in the grid
         $(this).jqGrid ('setSelection', this.rows[1].id, true);
    }
}

In the last version of the code I used the fact that this if the DOM element of the <table> which build the grid. So this.rows represents the array of <tr> elements (the rows of the grid). The first element this.rows[0] is the dummy row which has height: 0px and will be used to define the width of the columns. The second row (if any exists) this.rows[1] is the first row with the data. It's id is the rowid. You can read the answer for more information.

Community
  • 1
  • 1
Oleg
  • 220,925
  • 34
  • 403
  • 798