3
votes

I am trying to dynamically build the structure of a kendo-angular grid. My problem is that the grid options are not known when the k-options attribute is evaluated, so the grid is binding to ALL of the columns on the datasource.

Here is the HTML:

<div kendo-grid k-options="{{gridModel.options}}" 
    k-data-source="gridModel.myDataSource">
</div>

And here is the javascript in the controller:

// this is called after the api call has successfully returned with data
function getSucceeded(){
    ...
    $scope.gridModel.options = function(){
        // function that properly builds options object with columns, etc.
    }
    // this is just shown for example... the data is properly loading
    $scope.gridModel.myDataSource.data(ds.data()); 
}

The data is properly loading, but because gridModel.options was evaluated in the HTML prior to being set by the success method, it is essentially ignored and all of the columns from the datasource are being rendered.

This works like a champ when gridModel.options is static.

How can I defer the evaluation of k-options and/or force a reevaluation after they've been set by the controller?

3
This approach may help you stackoverflow.com/questions/19296500/…Chandermani
Thanks @Chandermani - this was helpful and ultimately part of the solution.Kevin

3 Answers

7
votes

I was able to figure it out. I had to do four things:

  1. Update my version of angularjs (I was on 1.08 which does not have the ng-if directive). I updated to 1.2.0rc3.
  2. Wrap my kendo-grid div in an ng-if div
  3. Invoke my function! I was just setting $scope.gridModel.options to a function - I needed to actually invoke the function so I'd be setting the variable to the value returned from the function.
  4. I had to update my angular.module declaration to include ngRoute (based on it being separated into it's own module in 1.2.x).

Here's the updated HTML:

<div data-ng-if="contentAvailable">
    <div kendo-grid k-options="{{gridModel.options}}" 
        k-data-source="gridModel.myDataSource">
    </div>
</div>

And here's the updated controller (not shown: I set $scope.contentAvailable=false; at the beginning of the controller):

// this is called after the api call has successfully returned with data
function getSucceeded(){
    ...
    $scope.gridModel.options = function(){
        // function that dynamically builds options object with columns, etc.
    }(); // <----- NEED to invoke function!!

    // this is just shown for example... the data is properly loading
    $scope.gridModel.myDataSource.data(ds.data()); 

    $scope.contentAvailable=true; // trigger the ng-if
}

I actually moved the function into a config file so I'm not polluting the controller with too much configuration code. Very happy to have figured this out.

2
votes

Here is a sample using 'Controller As' syntax, dynamic columns and paging.

var app = angular.module("app", ["kendo.directives"]);

function MyCtrl() {
  var colsList = [{
    name: "col1"
  }, {
    name: "col2"
  }, {
    name: "col3"
  }, {
    name: "col4"
  }];
  var gridCols = [];
  var iteration = 1;

  var vm = this;
  vm.gridOptions = {
    columns: gridCols,
    dataSource: new kendo.data.DataSource({
      pageSize: 10
    }),
    pageable: true
  };

  vm.buildGrid = function() {
    var data = {};

    vm.gridOptions.columns = [];
    for (var x = 0; x < colsList.length; x++) {
      if (iteration % 2 === 0 && x === colsList.length - 1) continue;

      var col = {};
      col.field = colsList[x].name;
      col.title = colsList[x].name;
      data[col.field] = "it " + iteration + " " + (1111 * (x + 1));
      vm.gridOptions.columns.push(col);
    }
    // add one row to the table
    vm.gridOptions.dataSource.add(data);
    iteration++;
  };
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<link rel="stylesheet" href="http://cdn.kendostatic.com/2015.1.318/styles/kendo.common.min.css" />
<link rel="stylesheet" href="http://cdn.kendostatic.com/2015.1.318/styles/kendo.default.min.css" />
<script src="http://cdn.kendostatic.com/2015.1.318/js/kendo.all.min.js"></script>

<body ng-app="app">

  <div ng-controller="MyCtrl as vm">
  <button ng-click="vm.buildGrid()">Build Grid</button>
  <div kendo-grid="grid" k-options="vm.gridOptions" k-rebind="vm.gridOptions"></div>
    </div>

</body>
0
votes

We can use the k-rebind directive for this. From the docs:

Widget Update upon Option Changes

You can update a widget from controller. Use the special k-rebind attribute to create a widget which automatically updates when some scope variable changes. This option will destroy the original widget and will recreate it using the changed options.

Apart from setting the array of columns in the GridOptions as we normally do, we have to hold a reference to it:

        vm.gridOptions = { ... };
        vm.gridColumns = [{...}, ... ,{...}];
        vm.gridOptions.columns = vm.gridColumns;

and then pass that variable to the k-rebind directive:

        <div kendo-grid="vm.grid" options="vm.gridOptions" k-rebind="vm.gridColumns">
        </div>

And that's it when you are binding the grid to remote data (OData in my case). Now you can add or remove elements to/from the array of columns. The grid is going to query for the data again after it is recreated.

When binding the Grid to local data (local array of objects), we have to somehow postpone the binding of the data until the widget is recreated. What worked for me (maybe there is a cleaner solution to this) is to use the $timeout service:

        vm.gridColumns.push({ ... });

        vm.$timeout(function () {
            vm.gridOptions.dataSource.data(vm.myArrayOfObjects);
        }, 0);

This has been tested using AngularJS v1.5.0 and Kendo UI v2016.1.226.