When in need of displaying columns with separate data – be it from a different source or based on the values of others – this new column type will prove truly helpful. Be sure to read the introductory blog on how to get started with jQuery Grid Unbound Columns. As promised we’ll go into greater detail into the options available to tweak the Ignite UI Grid’s Unbound Column feature to your own use case and needs.
Merge Unbound Columns
This is where it really gets interesting. Due to the very nature of the performed functions – bringing two or more distinct sources together in a single control’s view - as a developer you are given a additional option to control that. You can define the grid’s behavior using the MergeUnboundColumns property. It is relevant when defining a grid on the server-side with the ASP.NET MVC wrapper. The default option is false and what happens is that your unbound values get sent to the client as Metadata, rather than along with the actual data source records. You can take for example the previous blog sample. To make it clear here is how a part of the grid definition would look with the option off by default :
(It’s a little bit formatted and the actual records are collapsed so the structure can be visible, but rest assured they do not contain any of the unbound values)
Of course, if you take a closer look at the generated scripts you will also notice the above grid being initialized with the identical client property ‘mergeUnboundColumns’ being false. I did say it was a property meaningful when using the ASP.NET MVC helpers and it mostly is. What the client property does is being a flag for the grid if the data comes merged..or should the control do that on the client-side only in combination with remote data source. Otherwise, this property with local source won’t have any effect whatsoever. What all this means is that at the end of the journey the widget will have to do the merging anyway – that is how such values can normally participate in features such as sorting, filtering, etc.
Naturally, what makes the difference when setting this property to true is that the merging is performed on the server. Notice line 2 below:
- @(Html.Infragistics().Grid(Model).AutoGenerateColumns(false).AutoGenerateLayouts(true)
- .MergeUnboundColumns(true)
- .Columns(column =>
- {
- column.For(x => x.BusinessEntityID).HeaderText("B.E. Id").DataType("number");
- column.Unbound("name").HeaderText("Name").DataType("string");
- column.For(x => x.LoginID).HeaderText("Login Id").DataType("string");
- column.For(x => x.SickLeaveHours).HeaderText("Sick Leave Hours").DataType("number");
- column.For(x => x.VacationHours).HeaderText("Vacation Hours").DataType("number");
- })
- .Height("600px")
- .Features(feature =>
- {
- feature.Paging().Type(OpType.Local);
- feature.Sorting().Type(OpType.Local);
- feature.Filtering().Type(OpType.Local);
- feature.GroupBy().Type(OpType.Local);
- })
- .SetUnboundValues("name", (ViewBag.people as List<string>).Cast<object>().ToList())
- .DataBind()
- .Render()
- )
As a result data gets send as a totally normal set of records and the merge client property states true telling the widget not to look for values in the metadata as they are no longer there:
Merged Unbound columns distinctive property – unboundDS
The “unboundDS” property indicates that the column is indeed unbound. This is done, regardless of the client treating unbound columns as bound when ‘mergeUnboundColumns’ is true. Or should I say this is done exactly because of that. Have a look at what comes as a column definition in this case:
- {
- unbound : false,
- key : 'name',
- headerText : 'Name',
- dataType : 'string',
- unboundDS : true
- }
As the values for the column are already in the records the grid is not supposed to look for them elsewhere and the unbound setting is false. But then again the same false can be added to any other column with no side effects..So how do you know if there are actual unbound columns defined anyway? This is why the ‘unboundDS’ property is here for, to give a way for a client code to know if and which columns’ values are separate and most importantly to inform the grid that remote operations such as filtering and sorting (not supported by the feature) should not be allowed for that column.
Performance
As you can see the above-described configuration produces mostly the same results, however this time the served had to do the heavy lifting and the client enjoys not having anything to do with this and being blissfully unaware of unbound values. This is also very important as you have the option to distribute the load between client and server. Then again a fair warning follows – merging on either side may result in a performance hit if the original data source is way too big. This is because the whole data must be traversed and new unbound values added to it (or default null values if the list values are less). As you can imagine though, the server is usually the one is a more dangerous position as the large data manipulations can become pretty a serious problem when they have to be performed multiple times for multiple clients. A client doing this instead should take much less of an impact doing just its own work.
On a side note, remember the Set Unbound Values methods discussed in the previous blog and how it is strongly advisable to use the Dictionary overload as it is a performance gain? Well it’s even more true when using the merge option on the server - causes even more work as the server will be otherwise forced to create the bindings between values you provide and primary keys available. Of course, you are best to save it the trouble and assign them yourself, plus that way the order of the values won’t matter as much.
Client-side data manipulation
Last time we demonstrated the client-side version of the method to set unbound values and saw the additional route of applying a formula on the client. However, those are not the only ways to set unbound values. There’s also an interesting turn of events regarding the usage of the so far described merge functionality that requires some additional client work and therefore, being well equipped to manipulate data on the client is never a bad thing.
Merging Unbound Columns and setting values on data-bound client event
Let’s have an identical example as so far but this time lets enable the merge option along with using the formula field from last time. Nothing special it shouldn’t affect the end result, right?
- @(Html.Infragistics().Grid(Model).AutoGenerateColumns(false).AutoGenerateLayouts(true)
- .MergeUnboundColumns(true)
- .Columns(column =>
- {
- column.For(x => x.BusinessEntityID).HeaderText("B.E. Id").DataType("number");
- column.Unbound("name").HeaderText("Name").DataType("string");
- column.For(x => x.LoginID).HeaderText("Login Id").DataType("string");
- column.For(x => x.SickLeaveHours).HeaderText("Sick Leave Hours").DataType("number");
- column.For(x => x.VacationHours).HeaderText("Vacation Hours").DataType("number");
- column.Unbound("total").HeaderText("Total").DataType("number").Formula("calcTotal");
- })
- .Height("600px")
- .Features(feature =>
- {
- feature.Paging().Type(OpType.Local);
- feature.Sorting().Type(OpType.Local);
- feature.Filtering().Type(OpType.Local);
- feature.GroupBy().Type(OpType.Local);
- })
- .SetUnboundValues("name", (ViewBag.people as List<string>).Cast<object>().ToList())
- .DataBind()
- .Render()
- )
Well it did…
See when merge is enabled essentially you tell the grid values will be provided on/from the server and a JavaScript function for the formula is definitely not that. Also since as I explained values are assigned with null as default… that is what you end up getting this way. Can you still provide the same functionality with merged unbound columns? Sure! It’s not quite as simple, but possibly the most appropriate way to do this is to handle the client ‘dataBound’ event, at which point will have the data source object ready and you can directly assign values for unbound columns. Here’s the old formula function:
- function calcTotal(row, grid) {
- return row.SickLeaveHours + row.VacationHours;
- };
All that’s different is that you don’t have a function called each time with row values, but you can make it work again(if it was something big that you would not want to change or refactor):
- $("#Grid1").live("iggriddatabound", function (evt, ui) {
- // ui.owner is a reference to the actual grid widget
- var data = ui.owner.dataSource.data();
- $.each(data, function (index, record) {
- var unboundValue = calcTotal(record, ui.owner);
- record["total"] = unboundValue;
- });
- });
Once more, the data-bound event is the most logical place to execute that code as you have the data source, but the UI is not yet rendered, therefore this solution creates exactly the same results as before without noticeable difference for the end-user. Even so, it’s worth mentioning that you can enforce this method of assigning unbound values directly into the source at any point after this as an alternative to the SetUnboundValues method.
Get them!
Speaking of ‘SetUnboundValues ‘ the opposite operation is also available as a client method. There’s also a method to get the whole column even:
- var unboundColumn = $('#grid1').igGrid('getUnboundColumnByKey', 'name');
- var unboundValues = $('#grid1').igGrid('getUnboundValues', 'total');
The first provides you with the whole column with it’s properties and values:
And the values only method will give you just that array of values, however the column key parameter is optional and if not defined an object with all unbound columns’ value arrays will be returned instead.
It’s Complicated
Or is it? There are a number of questions in regard to what happens with unbound columns and advanced column features you’d normally use. This is especially true when you have a formula defined as well… well you can rest assured the functionality is all there. In the very special case of formula calculations to get the value – it is called before any formatting function or applying a template so for all intents and purposes it will work as with any other column. Just the column definition:
- column.Unbound("total").HeaderText("Total").DataType("number").Formula("calcTotal").Template("<span {{if parseInt(${total})/100 > 1 }} class='red' {{/if}}> ${total} / 100 </span>");
And it’s all working pretty well:
Same goes for formatter functions that are the more extended functionality, but largely similar to templating.
Resources and demos
- Two JSFiddle-s available for you to play with – the previous one and another built on top with added support for getting unbound values from columns and conditional templating discussed in this blog.
- A shared demo for this and the previous blog – and ASP.NET MVC project with various grid configurations described so far. Keep in mind most views use the MVC wrapper and require at least a trial version of Ignite UI on your machine – click the banner below to get yourself one.
- There are two demos available in our Samples: Unbound Column in the Grid and Unbound Column in the Hierarchical Grid.
- You can find general and control specific help in our Online Documentation
- You can find the client-side options and methods in the Ignite UI jQuery Grid API reference.
As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!
TL;DR | Summary
In this article we covered the usage of the Merge Unbound Columns property and its vary important performance implications. A look at the inner workings of the grid’s server model provided some insight on what’s really happening and how that would affect your application. Furthermore, we saw how the merge can affect functionality such as the formula and how to overcome that, along with some additional client-side manipulation. Hopefully the blog also cleared any possible questions as to whether Unbound columns work well with other Ignite UI Grid column options such as formatter functions or templates.
I’ll end this in a manner similar to how it was started – saying that such a feature concerning something as underlying as columns types in the jQuery Grid can affect multiple behaviors and features and is by a long shot not a simple functionality, as I hope it has been made clear by now. For that reason, there are still more interesting topics to be discussed for the future.