Updating individual fields in an ejGrid with Knockout.js

861 views Asked by At

The grid widget ('ejGrid') in the Syncfusion Essential JS library supports Knockout.js, however it only appears to work when the data source is an observable array of plain objects. How do I make it work if the data source is an observable array of view models? For example:

var Order = function (data) {
    var self = this;
    self.OrderID = ko.observable(data.OrderID);
    self.CustomerID = ko.observable(data.CustomerID);
    self.ShipCity = ko.observable(data.ShipCity);
    self.Freight = ko.observable(data.Freight);
    self.EmployeeID = ko.observable(data.EmployeeID);
};

var rows =
    [ {
        OrderID: 10248,
        CustomerID: "VINET",
        ShipCity: "Reims",
        Freight: 11.61,
        EmployeeID: 4
    }, {
        OrderID: 10250,
        CustomerID: "HANAR",
        ShipCity: "Charleroi",
        Freight: 65.83,
        EmployeeID: 2
    }, {
        OrderID: 10251,
        CustomerID: "VICTE",
        ShipCity: "Reims",
        Freight: 41.34,
        EmployeeID: 1
    }, {
        OrderID: 10252,
        CustomerID: "SUPRD",
        ShipCity: "Madrid",
        Freight: 51.3,
        EmployeeID: 3
    }, {
        OrderID: 10253,
        CustomerID: "HANAR",
        ShipCity: "Rio de Janeiro",
        Freight: 58.17,
        EmployeeID: 3
    } ];

var rows2 = $.map(rows, function (data) { return new Order(data); });

var gridData = {
    dataSource: ko.observableArray(rows),    
    dataSource2: ko.observableArray(rows2)
};

ko.applyBindings(gridData);

$('#remove').click(function () {
    // both these work fine, since the array is observable
    gridData.dataSource.pop();
    gridData.dataSource2.pop();
});

$('#mutate').click(function () {
    // mutating the field does nothing (since it's not observable)
    (gridData.dataSource())[0].OrderID = 999999; 
    // this should work, however the table is full of gibberish
    (gridData.dataSource2)()[0].OrderID(999999);
});

JSFiddle: http://jsfiddle.net/hrapq7m2/

In the above fiddle, the first grid uses an observable array of plain objects, which displays ok and we can remove rows via knockout using the 'Remove Row' button, however, mutating a field of a row via the 'Mutate Row' button doesn't work (and so it shouldn't). The second grid uses an observable array of view models, and so the implementation of the 'Mutate Row' button should work, however the grid appears to be attempting to display the observable objects instead of their current values.

2

There are 2 answers

0
Roy J On BEST ANSWER

If you call valueHasMutated on your data source, the observableArray will be re-examined and the grid will update.

$('#mutate').click(function () {
    // mutating the field does nothing (since it's not observable)
    (gridData.dataSource())[0].OrderID = 999999;
    gridData.dataSource.valueHasMutated();
});

With that, you don't need to have the individual data items be observables, but I came up with a kind of interesting way to make the ejGrid handle observables. If you define getters and setters for your properties using Object.defineProperty, the syntax for assignment is the same as if you have an ordinary variable, but it is implemented using the observable.

function observableToProperty(obj, data, propertyName) {
    var observable = ko.observable(data[propertyName]);
    Object.defineProperty(obj, propertyName, {
        get: observable,
        set: observable
    });
}
var Order = function (data) {
    var self = this;
    observableToProperty(self, data, 'OrderID');
    observableToProperty(self, data, 'CustomerID');
    observableToProperty(self, data, 'ShipCity');
    observableToProperty(self, data, 'Freight');
    observableToProperty(self, data, 'EmployeeID');
};

Now the update syntax is the same for either dataSource.

$('#mutate').click(function () {
    // mutating the field does nothing (since it's not observable)
    (gridData.dataSource())[0].OrderID = 999999;
    gridData.dataSource.valueHasMutated();
    // this should work, however the table is full of gibberish
    (gridData.dataSource2)()[0].OrderID = 999999;
    gridData.dataSource2.valueHasMutated();
});

There is, in this case, no advantage to using the observable, and the observables are completely private, so you can't even subscribe to them. But you could make them accessible and do something useful with them if you wanted. And you can use the properties in bindings just like the underlying observables.

There is no way to make the grid cells respond individually to updates of their contents without rewriting the ejGrid bindingHandler. Even wrapping it doesn't help, because you don't have access to what (if anything) it does at the cell level. So notifying the observableArray that something has changed is the best you can reasonably do.

http://jsfiddle.net/hrapq7m2/4/

1
Madhu On

1: "The second grid uses an observable array of view models, and so the implementation of the 'Mutate Row' button should work, however the grid appears to be attempting to display the observable objects instead of their current values."

Essential JavaScript Grid don’t have support to render the KO observable fields in the grid cells. But we can achieve your requirement with the following workaround.

//Workaround to show observable values in grid.
  var oldToString = Function.prototype.toString;

  Function.prototype.toString = function() {
    if(ko.isObservable(this))
       return this();
    return oldToString.call(this);
  }

Using the above workaround will make the observable values to be displayed in grid cells.

2: "The first grid uses an observable array of plain objects, which displays ok and we can remove rows via knockout using the 'Remove Row' button, however, mutating a field of a row via the 'Mutate Row' button doesn't work (and so it shouldn't)"

This is due to the fact that the ejGrid will maintain the length of the array and not the state of the array values and so changing the field value outside the grid will not update the UI. If you want the updated values to be showed in the grid, you have to refresh the grid as follows.

$('#mutate').click(function () {
// mutating the field does nothing (since it's not observable)
(gridData.dataSource())[0].OrderID = 999999; 
// this should work, however the table is full of gibberish
(gridData.dataSource2)()[0].OrderID(999999);

//Need to refresh grid to show updated value.
$("#Grid2").ejGrid("refreshContent");
});

Updated Fiddle: http://jsfiddle.net/hrapq7m2/3/