Amit
Amit

Reputation: 6294

Ag-Grid - Saving columns for future use

I am using the ag-grid for angular1, (and loving it), and I want my users to be able to reorgenize columns, change sortings, and everything, and that it will stay after a refresh. It should not be very hard, except that the columns are circular (contains pointers to themselves), and thus I cannot parse them.

Code:

var columnDefsKey = "columnDefs["+$rootScope.page+"]";
var savedColumns = localStorage.getItem(columnDefsKey);
function saveColumnsState() {
    var currentCol = vm.gridOptions.columnApi.getAllColumns();
    if (!angular.equals(currentCol, savedColumns))
        try {
            localStorage.setItem(columnDefsKey, JSON.stringify(currentCol));
        } catch (ex) {
            log(ex);
            log(currentCol);
        }
}

And:

onColumnEverythingChanged: saveColumnsState,
onColumnVisible: saveColumnsState,
onColumnPinned: saveColumnsState,
onColumnResized: saveColumnsState,
onColumnRowGroupChanged: saveColumnsState,
onColumnValueChanged: saveColumnsState,
onColumnMoved: saveColumnsState,
onColumnGroupOpened: saveColumnsState,

It fails on the "try" every time: TypeError: Converting circular structure to JSON(…) [Column, Column, Column, Column, Column, Column, Column, Column, Column, Column]

How can I do that? (save columns for later use)

If I manage to do that, I will be able to create several views without coding.

Upvotes: 1

Views: 1786

Answers (2)

Amit
Amit

Reputation: 6294

The way to achieve this was to build my own column model, that I can save and parse again, and in which to save only necessary properties.

This method is XSS vulnerable, as I am evaluating functions, but it is a working solution.

columnsApi: {
    key: null,
    grid: null,
    newColumnModel: {
        headerName: "",
        width: 200,
        valueGetter: "",
        filter: 'text',
        aggFunc: 'none',
        filterParams: {apply: true}
    },
    setKey: function (key) {
        this.key = key;
    },
    setGrid: function (grid) {
        this.grid = grid;
    },
    format: function (columns) {
        var format = [];
        angular.forEach(columns, function (col) {
            var colDef = {
                width: col.actualWidth,
                pinned: col.pinned,
                hide: !col.visible
            };
            format.push(angular.extend(col.colDef, colDef));
        });
        return format;
    },
    getIDs: function (columns) {
        var ids = [];
        angular.forEach(columns, function (col) {
            ids.push(col.colId);
        });
        return ids;
    },
    stringify: function (columns) {
        return JSON.stringify(columns, function (key, value) {
            if (typeof value === "function")
                return "/Function(" + value.toString() + ")/";
            return value;
        });
    },
    parse: function (string) {
        return JSON.parse(string, function (key, value) {
            if (typeof value === "string" &&
                value.startsWith("/Function(") &&
                value.endsWith(")/")) {
                value = value.substring(10, value.length - 2);
                return eval("(" + value + ")");
            }
            return value;
        });
    },
    add: function (column) {
        if (this.grid === null) {
            console.error("Assertion error: grid must not be null");
            return;
        }

        if(column.aggFunc == 'none')
            column.aggFunc = undefined;
        var groups = this.get().groups;
        var newColumns = this.format(getGridColumns(this.grid));
        newColumns.push(column);
        this.grid.api.setColumnDefs(newColumns);
        this.setGroups(groups);
    },
    save: function () {
        var self = this;
        if (this.key === null) {
            console.error("Assertion error: key must not be null");
            return;
        }
        if (this.grid === null) {
            console.error("Assertion error: grid must not be null");
            return;
        }

        var savedOptions = {
            columns: self.format(getGridColumns(self.grid)),
            groups: self.getIDs(self.grid.columnApi.getRowGroupColumns()),
            sorting: self.grid.api.getSortModel(),
            filter: self.grid.api.getFilterModel()
        };

        localStorage.setItem(this.key, this.stringify(savedOptions));
    },
    // Get function uses "eval" - XSS vulnerable.
    get: function () {
        if (this.key === null) {
            console.error("Assertion error: key must not be null");
            return;
        }
        var options = localStorage.getItem(this.key);
        if (options)
            options = this.parse(options);

        return options;
    },
    remove: function (field) {
        if (this.grid === null) {
            console.error("Assertion error: grid must not be null");
            return;
        }
        var newColumns = this.format(getGridColumns(this.grid));
        angular.forEach(newColumns, function (col, key) {
            if (col.field == field)
                newColumns.splice(key, 1);
        });
        this.grid.api.setColumnDefs(newColumns);
    },
    setGroups: function (groups) {
        var self = this;
        angular.forEach(groups, function (id) {
            angular.forEach(getGridColumns(self.grid), function (col) {
                if (col.colId == id)
                    self.grid.columnApi.addRowGroupColumn(col);
            });
        });
    }
}

This solution was written for Ag-Grid 5 I believe, and thus I am not sure if it still holds.

Upvotes: 0

Nimesh.Nim
Nimesh.Nim

Reputation: 388

you can get the better understanding of the issue from below link

Chrome sendrequest error: TypeError: Converting circular structure to JSON

Also check below reference

https://github.com/isaacs/json-stringify-safe

Upvotes: 2

Related Questions