IgniteUI™ controls on AngularJS with generated unique IDs

The control library from Infragistics, IgniteUI™, is tailored for general Javascript usage under JQuery. To use it within an AngularJS web app, they have made a directive wrapper called “Ignite UI directives for AngularJS” available on GitHub.

At the time of this writing I’m using IgniteUI version 15.1.20151.1005 and V1.0.0 of ignite-angularjs.js.

Directive without ID

The following code snippet uses the IgniteUI angular directives as indicated on its instructions. It declares the directive for an igGrid and configures it all using custom HTML elements. At the bottom of this article there is the full code for the final version.

<div ng-controller="ctrl1">
<ig-grid data-source="dataJS" auto-generate-columns="true" event-data-binding="gridCreated">
<features>
<feature name="Selection" mode="row"></feature>
</features>
</ig-grid>
</div>
<script>
angular.module('app', ['igniteui-directives']).controller('ctrl1', ['$scope', ctrl]);
function ctrl($scope)
{
$scope.dataJS = [
{"title":"This is the title 1", "subtitle":"This is the subtitle 1", "id":1},
{"title":"This is the title 2", "subtitle":"This is the subtitle 2", "id":2},
{"title":"This is the title 3", "subtitle":"This is the subtitle 3", "id":3}];

$scope.gridCreated =
function(evt, ui)
{
$scope.grid1 = ui.owner.element;
$scope.dataJS[0].title = "created!";
};

$scope.$watch('grid1',
function()
{
if ($scope.grid1)
$scope.grid1.igGridSelection('selectRow', 1);
});
}
</script>

One thing that is missing from the IgniteUI directives is to store the created object on some angular scope so it can be later accessed to issue commands or get information from it. One way to grab the created object is to hook an event handler. The handler will receive the object we need on ui.owner.element. The “created” event seems a good event for the purpose, but somehow I couldn’t get it to call my handler. Had to go with the “dataBinding” event which worked correctly. One problem with this approach is that I only have the object later on, after the object is initialized. This may pose a problem in some cases.

So, the code above seems to work, it does not have an ID and gets the created object into the angular controller. But, when I click on any row, there is an exception raised on the IngiteUI code and the clicked row is not selected:

Uncaught TypeError: Cannot read property 'match' of undefined; infragistics.log.js:407

The row selection starts working as soon as I put an ID on the igGrid element:

<ig-grid id="gr1" data-source="dataJS" auto-generate-columns="true" event-data-binding="gridCreated">

So, an ID must be in there. But giving it an ID (that must be unique in the all web page’s HTML) defeats the all purpose of modularity of AngularJS.

Directive with ID

Using an ID on the directive would provide a better way to grab the IgniteUI controller object to send commands to. And this object would be available early, unlike the previous approach.

One solution to make the ID globally unique is to generate it programmatically. To test the concept I tried using some programmatically provided ID:

<ig-grid ng-attr-id="{{gridId}}" data-source="dataJS" auto-generate-columns="true" event-data-binding="gridCreated">

And I must define gridId on the scope of the controller:

$scope.gridId = "gr1";

This didn’t work because the IgniteUI directives seem to run before the ngAttr and take the value unresolved. Changing the priority of those directives, implies changing their code and messing around with priorities is a thing I like to use in very last resource.

If one really doesn’t mind changing the IgniteUI directive’s code we could add a line to set an ID in the element the directive creates. It can generate a random one, or use a global (to the page) and central provider for unique IDs.

This can be done by locating the line that creates the element on the bottom of the “igniteui-angular.js” file:

element[controlName](options);

and adding the following line just before:

$(element).attr('id', ''+Math.round(Math.random()*100000));

This last option resolves the ID problem, but I still need to use the first technique to grab the grid object. And there are some cases where I will need to use the IgniteUI object before any event fires (where I could grab the object).

No Directive with ID

This solution uses the IgniteUI in its “native” javascript form, that is, without the IgniteUI angular directives, but it can all take place inside an angular scope and controller.

I’ll use the technique seen earlier to programmatically generate an ID for the IgniteUI placeholder object:

<table ng-attr-id="{{gridId}}"></table>

The main difference is that the IgniteUI object is created by traditional IgniteUI calls. Let’s see the full controller’s code:

function ctrl($scope, $timeout)
{
$scope.gridId = ''+Math.round(Math.random()*100000);
var gridObj = null;
var dataJS = [
{"title":"This is the title 1", "subtitle":"This is the subtitle 1", "id":1},
{"title":"This is the title 2", "subtitle":"This is the subtitle 2", "id":2},
{"title":"This is the title 3", "subtitle":"This is the subtitle 3", "id":3}];

$timeout(
function()
{
gridObj = angular.element('#'+$scope.gridId);
gridObj.igGrid(
{
dataSource: dataJS,
autoGenerateColumns: false,
columns: [
{ headerText: 'id', key: 'id', dataType: 'number',
template: '<a onclick="angular.element(\'#'+$scope.gridId+'\').scope().gridRemoveItem(${id});">X</a> ${id}' },
{ headerText: 'Title', key: 'title', dataType: 'string' },
{ headerText: 'Subtitulo', key: 'subtitle', dataType: 'string' }],
features: [{ name: "Selection", mode: "row" }]
});
});

$scope.gridRemoveItem = function(id)
{
alert("removing "+ id);
};
}

There is one thing of notice. The creation of the igGrid is made inside a function that is called with $timeout, because on the first run of the controller the $scope.gridId was just filled in, so the 

g-attr-id="{{gridId}}"

is not yet resolved. I will have to wait for the next angular digest cycle to go grab the element with that ID. This way I have full access, any time, to the IgniteUI object and it has (or can have) a global unique ID.

Also note the template technique for calling a $scope method. It feeds the grid ID to the angular.element() and then finds its $scope by using the angular scope() function.

Here is the full version on Plunker.

Have fun!

Leave a Reply

Your email address will not be published. Required fields are marked *