Ignite UI jQuery Grid Column Moving

Damyan Petev / Friday, February 22, 2013

Ignite UI jQuery Grid with Column Moving feature. Dropdown menu visible on the shot.The Grid is one of Ignite UI’s bread and butter controls and its feature set just.. keeps growing! I guess that fact that Infragistics has done quite a few grid controls over the years helps. Ome of the latest additions is the The Column Moving feature – it is still in a CTP stage for the current release (not for long I should add, 13.1 is coming our way!). It’s a really simple feature at first glance as with other ‘simple’ Grid features they turn out to be not that simple. In all seriousness, there’s barely such a thing as ‘simple’ grid feature because of the way event the smallest change can have impact on multiple other modules. And while there might be some odd end to polish, you can start using the feature now or at the very least get to know it a little.

The Column Moving allows the users to change the order of columns. From a user standpoint things are pretty well rounded – they can simple pick column headers and drag them to the desired position or open up a menu with convenient shortcuts as seen on the shot to the side here. Finally, an additional dialog with columns listed in order to be tweaked (interestingly enough inside is an Ignite UI Tree with drag and drop enabled nodes from the columns).

Getting started

As a developer you’d be pretty happy to find things are kept as easy as can be – enabling such a feature is straightforward – when referencing recourses make sure to include the column moving widget and the add the feature:

  1. $.ig.loader("igGrid.ColumnMoving", function () {
  2.     $("#grid").igGrid({
  3.         autoGenerateColumns: true,
  4.         dataSource: "@Url.Action("People","Home")",
  5.         features: [
  6.             {
  7.                 name: "ColumnMoving",
  8.                 mode: "deferred",
  9.                 type: "render"
  10.             }
  11.         ]
  12.     });
  13. });

And when using the ASP.NET MVC helper you don’t need to define recourses yourself:

  1. @(Html.Infragistics().Grid(Model).ID("Grid1").AutoGenerateColumns(true)
  2. .Features(feature =>
  3.     {
  4.         feature.ColumnMoving().Mode(MovingMode.Immediate).MoveType(MovingType.Dom);
  5.     })
  6. .DataBind()
  7. .Render()
  8. )

Perhaps you’ve notice the subtle differences between the two in term of settings – the Column Moving has two modes and two more modes we call types Smile. The DOM move type will instruct the feature to move only the affected table cells (<td> nodes) in the DOM tree. The other type will move the columns in the data source and then renders the grid again. More on that later on.

The immediate vs. deferred only affects the drag and drop interaction when moving and as you can imagine immediate just moves the column constantly as you drag it around. The deferred only moves on release and in the mean time it provides only an indicator where you’d be dropping that column:

Column Moving feature in deferred mode with an indicator while dragging.

Pretty simple, no? I find the immediate drag & drop reaction more immersive, don’t know just feels like that for me – but it comes at a price of doing a lot of extra movement in between, that can be otherwise omitted. The thing to note here is that the immediate mode (as duly noted in its jQuery API) doesn’t really mix well with the ‘render’ type – imagine dragging around the column header and have the grid underneath re-rendering constantly. It’s just too much of a hassle to attempt to compensate for that, so when mode is immediate, the type will only be ‘dom’ (even if you choose ‘render’).

Control

Out of the twofold nature of the interaction with the feature come two additional settings – you can turn on and off the the additional move menu globally, or both with the drag & drop capabilities on per column basis. The menu is integrated with the Feature Chooser, which is to say that if there are other features using it (such as Column Hiding), disabling the move menu will only remove the relevant parts to that feature. This is done globally though the ‘addMovingDropdown’ setting:

  1.     //....
  2.     features: [
  3.         {
  4.             name: "ColumnMoving",
  5.             addMovingDropdown: false
  6.         }
  7.     ]
  8.     //.....

The per-column settings also let you can disallow the drag and drop functionality for the column (since it’s based on jQuery UI Draggable it means that the widget will not be initialized for the column) in addition to the menu:

  1.         //....
  2.         features: [
  3.             {
  4.                 name: "ColumnMoving",
  5.                 columnSettings: [
  6.                     {
  7.                         columnKey: "Demographics",
  8.                         allowMoving: false
  9.                     }
  10.                 ]
  11.             }
  12.         ]
  13.         //....

Note this doesn’t really fix the column in it’s place – as I explained it merely doesn’t create the draggable widget and Feature chooser menu for that column. However, since other columns can move, they can and probably will push that column out of it’s place. Also the API method can move that column regardless.

Notes, tips and tricks

The feature offers a single method, but a ton of settings and events you can make use of in creating amazing grid experiences. There are some options that probably didn’t make it to the jQuery API page(happens, being CTP and all):

The hidden settings

The column header users drag can be made transparent though the ‘dragHelperOpacity’ as it is exactly that the helper for the jQuery UI Draggable and this directly corresponds to the opacity property.

There are two tolerance settings for this feature and they are both only regarding the Drag&Drop interaction side of Column Moving – besides letting you dragging the header around the feature is also kind enough to scroll the grid for you when you go near the edge. The edge is actually a distance threshold defined by the ‘movingScrollTolerance’ property (length (in pixels) below which horizontal scrolling occurs) and supported by ‘scrollDelta’ that dictated how much should be scrolled each time.

Then you also have the ‘movingAcceptanceTolerance’ which is how the feature checks if you are close to a possible column drop point and the column/indicator should be moved there. I think a visual explanation will be great for this. The default accept tolerance is 20px which means that if the edge of the helper moves closer that that to some edge, that edge will be accepted as target position for column moving, see what I mean:

The Column Moving accept tolerance of 20px visualized as a green-ish area, that once crossed by the drag helper will cause a colum/incidator move.

There’s a very important note for the tolerance settings – use wisely and with caution – imagine what will happen if you crank up the tolerance too much, like wider than your columns? The feature will totally go crazy and drag column moving will produce highly unpredictable results! I’d say those 20px as pretty confortable, but hey with great settings power comes great… you get the point!

The ‘hideHeaderContentsDuringDrag’ which is why the actual column header above is empty when dragging.

Multi-Column headers and hierarchy

The Column Moving acts really well with this (also new) feature and also plays nice with the Hierarchical version of the grid. The drag helper/header is restricted within the confines of it’s respective grid (which prevents child-parent moves in the Hierarchical Grid) and won’t be accepted outside it’s column group, so the Multi header settings are respected. There are some gotcha-s here – the API move method will believe you more that the user and attempt to move whatever you tell it to wherever – yes you can move entire header by identifier, but careful with indexes – they include the group and vary inside it as well. I might do a whole blog on that later on, but for now just thoroughly test if you combine those features.

Performance

So, back to those move types. As you might've guessed there’s a reason why the move type is exposed to the developer – it has performance implications because of the ways the actual moving is performed. Remember how data is laid in the HTML table – by columns? Of course not – it’s by rows. That means finding the right cell, detaching it and re-attaching it before/after a different target cell. That’s not that bad, but consider it is done for every row each time you move a column! It can get pretty intensive.

The re-render, on the other hand, destroys most of the grid layout and based on the already updated data source renders the data table again. So how’s that for intensive? Well, it depends kind of… Our developers noted this and I did some very quick tests to confirm this:

As a general rule of thumb, DOM Column Moving is faster for most browsers, but (surprise, surprise!) the ‘render ‘ is actually faster for IE. And, yes, I’ve tested IE10 thinking there’s probably some sort of an error, but even with small sample numbers the difference was so huge that there’s no way it’s wrong. No idea why – is it the querying of a whole bunch of elements or the DOM manipulation that’s the bottleneck, but it turns out re-rendering is faster for IE.

But, hey, that’s just interesting info – there’s no reason for you to worry or even consider this an issue – there’s an easy solution – please, please use Paging! With reasonable page size the time it takes to move a column of 2000 rows takes a plunge down to a snappy, pretty much instant, 50ms range. Trust me when I say it makes all the difference and it’s an absolute must if you plan on a lot of data. And if for whatever reason you need it all in a single view(page) then enable Row Virtualization! Paging kind of delivers the same functionality and the conclusion is an old one – virtualization is totally awesome with big data and a virtualized grid handles thousands of records like a boss!

Making use of events to enhance functionality

Personally there’s something that bugs me about the immediate mode of the feature.. When dragging columns around in a very demo grid, with all healthy mix of easily distinguishable text/numbers mix in the columns, it’s all good and merry. But when you hit a grid with like all numbers – like it’s very likely to happen in some scenarios – I find it really hard to track my column around. There’s no indicator with immediate, the only notice really is the empty header if hasn’t been disabled or the same text following you around. Good thing the Column Moving Events are many and give you precious information needed to control and/or enhance functionality.

Well I figured I can show you how to events and enrich the experience at the same time. I’ve decided to cheat a bit and steal the Sorting feature indication by highlighting the column currently moving with the following class:

  1. <style>
  2.     .ui-iggrid td.ui-state-moving {
  3.         background: 0;
  4.         background-color: #c2e8f8;
  5.         color: #333;
  6.         font-weight: normal;
  7.         text-shadow: 0 1px 0 rgba(255,255,255,0.8);
  8.         border-color: #b3e2f6;
  9.         border-width:  1px;
  10.     }
  11. </style>

Two things to note here – this is almost identical to the highlight Sorting uses, just renamed so it won’t mess with the feature’s CSS, but if you absolutely know you won’t be using Sorting or it’s highlight, it’s safe to just apply ‘ui-state-highlight’. And secondly, if you are using it with sorting CSS you might want to consider some different colors. Having said that, applying the class is extremely easy:

  1.         features: [
  2.             {
  3.                 name: "ColumnMoving",
  4.                 mode: "immediate",
  5.                 columnDragStart: function (event, ui) {
  6.                     ui.owner.element.find('tr td:nth-child(' + (ui.columnIndex + 1) + ')').addClass("ui-state-moving");
  7.                     colMovingKey = ui.columnKey;
  8.                 },
  9.                 columnDragEnd: function (event, ui) {
  10.                     var grid = $(event.target).data('igGrid');
  11.                     var newindex = grid.options.columns.indexOf(grid.columnByKey(colMovingKey)) + 1;
  12.                     grid.element.find('tr td:nth-child(' + newindex + ')').removeClass("ui-state-moving");
  13.                 }
  14.             }
  15.         ]

The drag start and end events correspond directly to the jQuery UI Draggable events, and they even relay most of the common arguments. The other events include a column moving and moved events to react before and after a move, and then a whole bunch of events related to the additional menu and dialog.

The above snippet is only fit for a very basic layout (the demos include a somewhat acceptable version with multi-column headers as well, so check them out). The result looks something like this – just imagine a much bigger grid and better image quality:

Using the events of Column Mvoing to add a highlight while dragging.

As I always say, it’s not as much about modifying the feature, but showing you can quickly apply enhancements and control at various points of interest. And it’s that easy!

Resources

So besides all the links sprinkled all over, here’s a quick and useful list of info and demos:

Donwload your Ignite UI Free Trial now!

I’d love to hear some thoughts, so leave a comment down below or @DamyanPetev.

And as always, you can follow us on Twitter @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!