New to Kendo UI for jQuery? Download free 30-day trial

Persist the State of the Grid alongside the Function Handlers

Environment

Product Grid for Progress® Kendo UI®

Description

By default JSON.stringify() cannot serialize function definitions. Event handlers and other similar Grid configurations are lost when the state is persisted with getOptions and setOptions.

Solution

Implement a custom JSON reviver which can serialize and deserialize the function definitions.

<script type="x-kendo/template" id="template">
    <button class='k-button' onclick='kendo.alert("WORKS")'>My Button</button>
</script>

<button id="save">Save Options</button>
<button id="set">Set Options</button>
<div id="grid"></div>
<script>
    extendJsonParsing();

    $(document).ready(function () {
        $("#grid").kendoGrid({
            dataSource: {
                type: "odata",
                transport: {
                    read: "https://demos.telerik.com/kendo-ui/service/Northwind.svc/Orders"
                },
                schema: {
                    model: {
                        fields: {
                            OrderID: { type: "number" },
                            Freight: { type: "number" },
                            ShipName: { type: "string" },
                            OrderDate: { type: "date" },
                            ShipCity: { type: "string" }
                        }
                    }
                },
                pageSize: 20,
                serverPaging: true,
                serverFiltering: true,
                serverSorting: true
            },
            // Databound function that must also be saved in the options of the user.
            dataBound: function (e) {
                console.log("The DataBound event was fired.");
            },
            toolbar: kendo.template($("#template").html()),
            height: 550,
            filterable: true,
            sortable: true,
            pageable: true,
            columns: [{
                field: "OrderID",
                filterable: false
            },
            /* Editable function that must also be saved in the options of the user */
            { field: "Freight", editable: function () { return false; } },
            {
                field: "OrderDate",
                title: "Order Date",
                format: "{0:MM/dd/yyyy}"
            }, {
                field: "ShipName",
                title: "Ship Name"
            }, {
                field: "ShipCity",
                title: "Ship City"
            }
            ]
        });
    });

    $("#save").on("click", function (e) {
        e.preventDefault();
        var options = $("#grid").data("kendoGrid").getOptions();
        // Stringify the grid options.
        var stringOptions = JSON.stringify(options);
        // Save them in the local storage.
        localStorage["kendo-grid-persist-options"] = stringOptions;
    });

    $("#set").on("click", function (e) {
        e.preventDefault();
        var options = localStorage["kendo-grid-persist-options"];
        if (options) {
            // Parse the options using the custom reviver.
            var parseOptions = JSON.parse(options, Function.deserialize);

            var grid = $("#grid").data("kendoGrid");
            // Set the options back to the Grid.
            grid.setOptions(parseOptions);
        }
    });

    function extendJsonParsing() {
        Function.prototype.toJSON = function () {
            // Retrieve the function definition.
            var output = this.toString();
            // The default schema.model.fields parsers cannot be serialized. You need to exclude them.
            var parsers = ["number", "date", "boolean", "string"];

            if (
                this.name &&
                (new RegExp('^\\s*' + this.name + '\\s*([(])')).test(output)
            )
                output = 'function ' + output;

            // Stringify the function as long as it isn't one of the default parsers.
            if (!parsers.includes(this.name))
                return Function.prototype.toJSON.flag + output;
        };
        Function.prototype.toJSON.flag = '_FUNC';
        Function.deserialize = function (key, data) {
            // Check if the current field that is being deserialized is a function or not.
            if (!Function.deserialize.check.test(data))
                return data;

            // If it is a function, remove the flag from it.
            data = data.replace(Function.deserialize.check, '');

            // Parse the string back to a regular function.
            return new Function(
                'return (' + data + ').apply(this, arguments);'
            );
        };
        Function.deserialize.check = new RegExp('^' + Function.prototype.toJSON.flag);
    }
</script>
In this article