jQuery Hierarchical Grid - Handling Events

Jordan Tsankov / Monday, January 30, 2012

Just like every other Infragistics jQuery control, the hierarchical grid comes with events you may attach handlers to. Due to the nature of the grid, however, it might prove difficult to determine exactly where was an event fired. You will probably have several child grids ( which are also called "column layouts" ) and each one is a suspect for triggering an event. In this post, I will detail how to get basically all the information you would need about both the row that triggered an event and the grid in which that row resides in.

Let’s get on with it !

How to do it

The trick does not lie in binding the events – you do that in the traditional way. Take a look at what events are available on this page, then bind some handlers to the ones you want. Here are two ways of doing it:

   1: // setup the handlers while initializing the grid
   2:  
   3: grid.igHierarchicalGrid({
   4:     dataSource: source,
   5:     dataSourceType: "json",
   6:     width: 500,            
   7:     rowExpanding: function(evt, ui) { /* handling logic goes here */ },
   8:     rowExpanded: function (evt, ui) { /* handling logic goes here */ },
   9:     rowCollapsing: function (evt, ui) { /* handling logic goes here */ },
  10:     rowCollapsed: function(evt, ui) { /* handling logic goes here */ }
  11: });
  12:  
  13: // or setup handlers post-initialization
  14: grid.bind("ighierarchicalgridrowexpanding", function(evt, ui) { /* handling logic goes here */ } );
  15: grid.bind("ighierarchicalgridrowexpanded", function(evt, ui) { /* handling logic goes here */ } );
  16: grid.bind("ighierarchicalgridrowcollapsing", function(evt, ui) { /* handling logic goes here */ } );
  17: grid.bind("ighierarchicalgridrowcollapsed", function(evt, ui) { /* handling logic goes here */ } );

The key is working with properties within the event parameters – you probably saw some listed on the “Events” page linked above. The most crucial one is ui.parentrow – this property references to the specific row within the hierarchical grid that was expanded/collapsed. You might be thinking that ui.owner would help us out - ui.owner always references the igHierarchicalGrid widget. That reference will never change, no matter the real source of the event. ui.parentrow changes, though ! Utilizing this fact, we can pinpoint the child grid which holds the row, we can get that grid’s level ( how further down it is from the root ) as well as the row’s index.

Here’s a basic way of finding out the event sender:

   1: function log(evt, ui) {
   2:     var row = ui.parentrow[0]; // this gets the element of the row that triggered the event
   3:     var event_type = evt.type; // the type of event that was triggered
   4:     var grid = $(row).closest("table"); // this is how you get EXACTLY the grid that fired the event
   5:     var grid_id = $(grid).attr("id");
   6:     var level = grid.attr("data-level"); // and its level in the hierarchy , 0 being root
   7:     var row_index = $(row).parent().children("tr:not(tr[data-container])").index(row); // index of the row
   8: }

You can bind this handler to any of the hierarchical grid’s events and they will get processed ( although you will see no output – if you wish to use the snippet above you must write a line that somehow uses all the available information ).

On line 2 we get the DOM object representing the row that triggered the event. If you would rather get the row wrapped in a jQuery object, use ui.parentrow instead.

Line 3 determines the event type that was triggered – ultimately,  you won’t really need this since chances are you will have different handles for different events.

Line 4 is a little tricky. We said we’d want to know which grid the event came from – and if you looked at the API, you might’ve seen a ui.owner property that probably sounded appealing. This will only return a reference to the hierarchical grid, not to the sender of the event. To get that grid, we’ll use jQuery’s .closest() method. What it will do is it will iterate through the target’s ancestors and match elements against the selector you’ve provided. The result will be the first matched element. Since we know that rows are stored within tables, we give the .closest() method a selector that will locate the first table element. This will be the grid that our event came off from.

Line 6 shows how to fetch the position of the event grid in the hierarchy – 0 is root level, with the number increasing as you go farther down the hierarchy.

Line 7 is a little tricky too. What we’re doing there is not entirely obvious through the code, although it’s clear we’re looking for some element’s index. What might be troublesome to understand at first is the selector we’ve used – tr:not(tr[data-container]). If we were to rephrase this one line with words, it would sound like this “I want to select the parent of my event row, then get a list of all its children that are table rows AND DO NOT HAVE data-container attribute. Then in that list I want to find the position of my event row.” Why don’t we want table rows that have the “data-container” attribute, you might ask? The rows that have data-container attribute are basically rows that hold sub-grids – they represent the columnLayout property. I think this will make sense in the following set of pictures:

base_hgbase_hg_html

Here you see a standard hierarchical grid that has just been rendered – no rows are expanded. Up above is the html that is generated for that grid – three <tr> elements, each representing one row in the grid.

Now here’s what happens as soon as you expand one of the rows. Pay attention to the html image – there will be a line that is outlined, it will show where the child grid is being placed.

expanded_hg

expanded_hg_html

The sub-grid labeled “Developers” is rendered inside the outlined row above.

Now it will be clearer to explain why we don’t want to select these rows when looking for a row’s index – because the rows with the data-container attribute simply do not contribute to the grid they’re in – they hold none of that grid’s data. A row marked with data-container holds the columnLayouts for the expanded row just above it.

Events from features

When you have different features enabled on your hierarchical grid, you may also want to take different actions depending on which grid was the feature triggered on. Luckily, it is much easier to obtain important information for the event - mostly because when you have features and even when they are inherited (the feature is defined for the root level with the inherit option set to true), there is still a separate widget for each inherited feature for every grid (root or column layout) in the hierarchical grid.

   1: // the exact same function can be used for a MVC hierarchical grid
   2: $("#gridTarget").live("iggridsortingcolumnsorted", function (evt, ui) {
   3:     var sorted_id = ui.owner.grid.id();
   4:     if (sorted_id.indexOf("Developers") >= 0)
   5:         $("<span>Sorted fired from " + sorted_id + "</span><br /><br />").appendTo("#infoTarget").css("color", "blue");
   6:     else if (sorted_id.indexOf("Tasks") >= 0)
   7:         $("<span>Sorted fired from " + sorted_id + "</span><br /><br />").appendTo("#infoTarget").css("color", "green");
   8:     else if (sorted_id.indexOf("Checkins") >= 0)
   9:         $("<span>Sorted fired from " + sorted_id + "</span><br /><br />").appendTo("#infoTarget").css("color", "red");
  10:     else if (sorted_id.indexOf("gridTarget") >= 0)
  11:         $("<span>Sorted fired from " + sorted_id + "</span><br /><br />").appendTo("#infoTarget").css("color", "orange");
  12: });

In the snippet above, we've decided to add an event handler to the columnSorted event of the Sorting feature. Then directly on line 3, you see how much easier it is to obtain the grid's ID and then branch off into different cases based on that ID. Note that the ID is either the one of your root grid, or it contains the *key* of one of the column layouts (sub-grids). It is so exactly because you have separate feature widgets for every grid - regardless of whether it was inherited or not. Also, just like stated in the snippet, this same manner of registering event handlers ( via the .live() method ) applies to MVC hierarchical grids as well. With them, your only option is using the .live() jQuery method.

You can see the snippet above in action if you download my sample project - there is a link located in the section below.

Conclusion

In the post above we’ve seen how to locate the exact element that triggered an event within an Infragistics jQuery Hierarchical Grid. When dealing with the hierarchical grid events, the process is a bit tricky. However, dealing with general features and their events, you're going to have to do a lot less work to handle the events.You may see a working sample by downloading my project here.

InfragisticsHG_events.zip