v7.3.0
SupportExamplesFree Trial

Upgrade guides for Grid v4.0.0+

Grid v4.0.0

Dropped support for Edge 18 and older

We are not actively removing the fixes we have in place yet, but moving forward we will no longer add new fixes for versions of Edge <= 18. If you are using an old version of Edge, we strongly encourage you to update to a new blink based version.

Existing fixes are planned to be removed in version 5.0.

New grid.lite.umd.js bundle to use with Angular

Bryntum Grid is delivered with a new UMD package without polyfills. This was done to avoid conflicts with any external Promise polyfills, such as ZoneAwarePromise from zone.js for Angular applications. It fixes this runtime error:

Zone.js has detected that ZoneAwarePromise (window|global).Promise has been overwritten.
Most likely cause is that a Promise polyfill has been loaded after Zone.js 
(Polyfilling Promise api is not necessary when zone.js is loaded. If you must load one, do so before loading zone.js.)

We recommend that all your Angular applications should be upgraded to use the new grid.lite.umd.js bundle. Also, there should be no polyfills imported in app/src/polyfills.ts after import 'zone.js/dist/zone';. We have updated all our Angular examples to use this new bundle. Make sure to update all usages of grid.umd.js to grid.lite.umd.js within your application to avoid Bundle included twice runtime error.

Old code:

import { Grid, Model, Store } from 'bryntum-grid/grid.umd.js';

New code:

import { Grid, Model, Store } from 'bryntum-grid/grid.lite.umd.js';

If you wish to have IE/Edge compatibility, please follow the instructions in app/src/polyfills.ts.

State

State was refactored to use a slightly different format which might affect end users, any stored subGrid width/collapsed states will be reset. This is of course a temporary problem, as soon as users save state again, all will work as expected.

Grid extends Panel

The Grid class (and therefore Scheduler and Gantt) now extends Panel. This means that they can have embedded tbar and bbar and header components to offer a richer UI right inside the Grid.

new Grid({
    appendTo : myElement,
    store    : myStore,

    // Will show a header containing the title
    title : 'My Data Grid',

    // Will show a top toolbar. Note that 'loadButton' will be available in the Grid's `widgetMap`
    tbar    : {
        items : {
            loadButton : {
                text : 'Reload',
                onClick() {
                    myStore.reload();
                }
            }
        }
    },
    columns : [/*...*/]
});

Store can accept a raw JSON string

Store's json property now accepts a JSON string and applies it to its data property, so the json property can be bound to a JSON data source to keep a grid's data up to date.

The json accessor yields a JSON string representation of the current data load.

WidgetHelper and BryntumWidgetAdapter

Registration of Widget classes for subsequent resolution through the type property of config objects has been simplified. The "Adapter" concept has been removed. Widget classes now register themselves with the Widget base class which is where they can also be looked up. When creating a custom widget, implement the static get type property to return the type name. And at the end simply call MyWidgetClass.initClass() to have the new Widget class register itself.

Old code:

class MyWidget extends Widget {

}

BryntumWidgetAdapterRegister.register('mywidget', MyWidget);

New code:

class MyWidget extends Widget {
    static get type() {
        return 'mywidget';
    }
}

MyWidget.initClass();

Fewer widgets are auto imported with the removal of the Adapter which imported a base set of Widgets. This means that when not using a bundle, application code must import the widgets it uses. It also means that custom builds may be smaller by including only what is used.

If your code previously had:

import 'lib/Core/adapter/widget/BryntumWidgetAdapter.js';
import WidgetHelper from 'lib/Core/helper/WidgetHelper.js';

WidgetHelper.append([
    {
        type : 'button',
        text : 'Click'
    }
], /*...*/);

It should now instead not import the adapter, and instead directly import the button:

import WidgetHelper from 'lib/Core/helper/WidgetHelper.js';
import 'lib/Core/widget/Button.js';

WidgetHelper.append([
    {
        type : 'button',
        text : 'Click'
    }
], /*...*/);

Context menu refactoring

Basic class

New abstract ContextMenuBase class was introduced. It has common logic for all context menu features. It creates a new menu instance, handles event firing, and provides basic functions to work with the menu.

ContextMenu feature

ContextMenu feature was replaced by CellMenu and HeaderMenu. So now context menu is configured for cells and headers independently. Items are named objects now:

Old code:

const grid = new Grid({
    features : {
        contextMenu : {
            headerItems : [
                { text : 'My header item', onItem : () => {/*...*/} }
            ],

            cellItems : [
                { text : 'My cell item', onItem : () => {/*...*/} }
            ],

            processCellItems({ items, record }) {
                if (record.cost > 5000) {
                    items.push({ text : 'Split cost' });
                }
            }
        }
    }
});

New code:

const grid = new Grid({
    features : {
        headerMenu : {
            items : {
                myItem : { text : 'My header item', onItem : () => {/*...*/} }
            }
        },

        cellMenu : {
            items : {
                myItem : { text : 'My cell item', onItem : () => {/*...*/} }
            },

            processItems({items, record}) {
                if (record.cost > 5000) {
                    items.costItem = { text : 'Split cost' };
                }
            }
        }
    }
});

showRemoveRowInContextMenu config

Grid's showRemoveRowInContextMenu config was deprecated in favour of CellMenu config. So to remove a default item please follow this way:

Old code:

const grid = new Grid({
    showRemoveRowInContextMenu : false
});

New code:

const grid = new Grid({
    features : {
        cellMenu : {
            items : {
                removeRow : false // `removeRow` is a key of default "Remove row" item
            }
        }
    }
});

cellMenuItems and headerMenuItems column configs

Column's cellMenuItems and headerMenuItems configs were turned into named objects:

Old code:

const grid = new Grid({
    columns : [
        {
            text            : 'First name',
            field           : 'firstName',
            headerMenuItems : [
                { text : 'Column specific header item' }
            ],
            cellMenuItems : [
                { text : 'Column specific cell item' }
            ]
        }
    ]
});

New code:

const grid = new Grid({
    columns : [
        {
            text            : 'First name',
            field           : 'firstName',
            headerMenuItems : {
                firstColumnItem : { text : 'Column specific header item' }
            },
            cellMenuItems : {
                firstColumnItem : { text : 'Column specific cell item' }
            }
        }
    ]
});

Context menu events

Some of Grid's events provided by ContextMenu feature were replaced by CellMenu and HeaderMenu features. Here is the list of changes:

  • cellContextMenuBeforeShow -> cellMenuBeforeShow;
  • cellContextMenuShow -> cellMenuShow;
  • headerContextMenuBeforeShow -> headerMenuBeforeShow;
  • headerContextMenuShow -> headerMenuShow;
  • contextMenuItem -> cellMenuItem and headerMenuItem;
  • contextMenuToggleItem -> cellMenuToggleItem and headerMenuToggleItem;

For example:

Old code:

const grid = new Grid({
    listeners : {
        cellContextMenuBeforeShow   : () => {/*...*/},
        cellContextMenuShow         : () => {/*...*/},
        headerContextMenuBeforeShow : () => {/*...*/},
        headerContextMenuShow       : () => {/*...*/},
        contextMenuItem             : () => {/*...*/},
        contextMenuToggleItem       : () => {/*...*/}
    }
});

New code:

const grid = new Grid({
    listeners : {
        cellMenuBeforeShow   : () => {/*...*/},
        cellMenuShow         : () => {/*...*/},
        cellMenuItem         : () => {/*...*/},
        cellMenuToggleItem   : () => {/*...*/},
        headerMenuBeforeShow : () => {/*...*/},
        headerMenuShow       : () => {/*...*/},
        headerMenuItem       : () => {/*...*/},
        headerMenuToggleItem : () => {/*...*/}
    }
});

Model Field Inheritance

Fields defined in a derived class Model that coincide by name with a field declared in a super class will now only override those field config properties specified by the derived class. This allows derived classes to adjust a field definition in one way, say to change the default value and still inherit other field properties, such as a convert function.

For example:

    class ModelOne extends Model {
    static get fields() {
        return [
            { name : 'barcode', convert : v => String(v) }
        ];
    }
}

class ModelTwo extends ModelOne {
    static get fields() {
        return [
            { name : 'barcode', defaultValue : 'ABC123' }
        ];
    }
}

In previous releases, ModelTwo would have had to redefine the convert config for the barcode field.

Renamed CSS themes

The Default, Light and Dark themes were renamed to Classic, Classic-Light and Classic-Dark. This change highlights the fact that they are variations of the same theme, and that it is not the default theme (Stockholm is our default theme since version 2.0).

If you are using one of these themes, you will have to adjust your css import to match the new name.

Old code:

<link rel="stylesheet" href="build/grid.dark.css" id="bryntum-theme">

New code:

<link rel="stylesheet" href="build/grid.classic-dark.css" id="bryntum-theme">

If you have theme specific selectors in your code/CSS you have to adjust those also:

Old code:

.b-theme-dark {
/*...*/
}

New code:

.b-theme-classic-dark {
/*...*/
}

RowReorder feature

The RowReorder feature now supports dragging multiple rows and the record property previously part of the events fired is now renamed to records and is an Array of records.

Old code:

grid.on('gridRowDrop', event => {
    console.log(event.context.record.name);
});

New code:

grid.on('gridRowDrop', event => {
    console.log(event.context.records[0].name);
});

Deprecations

StringHelper

The capitalizeFirsLetter() and lowerCaseFirstLetter() functions in StringHelper was deprecated in favor of capitalize() and uncapitalize(). The old functions are directly swappable for the new. If you are using one of these functions, please rename your usages.

Old code:

StringHelper.capitalizeFirstLetter('batman'); // Batman
StringHelper.lowercaseFirstLetter('Joker');   // joker

New code:

StringHelper.capitalize('batman');  // Batman
StringHelper.uncapitalize('Joker'); // joker

Accessibility

A change was made to allow keyboard navigation from a Grid column header to the rows below using the down arrow key. Earlier this triggered the column menu to show, the menu can be triggered with the space bar key. Pressing down arrow from a grid column header, will now put focus at the first fully visible cell of that column.

Grid v4.0.6

Grid#deselectAll behavior change

In previous versions, it was not possible to clear selections completely if the view was filtered. As of 4.0.6, the default behavior of deselectAll() is to clear all selections. If you wish to keep the old behavior of keeping selections of filtered out rows, pass true as the first and only param

Existing fixes are planned to be removed in version 5.0.

Old code:

grid.deselectAll(); // Previously deselected only rows part of the current filtered row set 

New code:

grid.deselectAll(true); // Pass true to clear selections in current rowset but keep earlier selections

Grid v4.1.0

Store move method accepts an array

In version 4.1.0, store.move() accepts an array in order to move multiple records in one atomic operation. Due to this, the resulting move event now has a records property which is the array of records moved. The record event property is deprecated and will be removed in version 5.0.0.

ColumnPicker Feature now has ability to add new columns based on defined fields in the Model

By configuring the ColumnPicker feature with createColumnsFromModel : true, fields defined in the grid's store's modelClass appear in the column menu, and can be added as new columns.

features : {
    columnPicker : {
        createColumnsFromModel : true
    }
}

By default the column header is the uppercased version of the field name. To define a field with a custom column header text:

fields [{
    name : 'priority',
    text : 'Event Level'
}]

To exempt a field from being added to the column menu by this feature, configure it with internal : true:

fields [{
    name     : 'image',
    internal : true
}]

Dropped support for RequireJS

In version 4.1.0 we have removed the RequireJS demo from the project, since it is an outdated technology. We instead recommend using modern ES6 module imports which is supported in all modern browsers (Chrome, FireFox, Safari, Edge (chromium)).

You could easily upgrade old application code to use new technology.

Old code:

index.html

<script data-main="scripts/app" src="scripts/require.js"></script>

scripts/app.js

requirejs.config({
    paths : {
        grid : '../../../build/grid.umd'
    }
});

requirejs(['grid'], function(bryntum) {
    new bryntum.Grid({
        // config here
    });
});

New code:

index.html

<script type="module" src="scripts/app.js"></script>

scripts/app.js

import { Grid } from '../../../build/grid.module.js';

new Grid({
    // config here
});

Prevent listeners from an on-function

Returning false from an on-function now prevents any attached listeners from being called. For example:

const button = new Button({
    onClick() {
        return false;
    },

    listeners : {
        click() {
            // Won't run when button is clicked, prevented by returning false in onClick() above 
        }
    }
});

Prior to this change, the return value of on-functions was not handled at all. We judge the risk of this change as low, but if you are using both on-functions and listeners on the same instance and are returning a value from the on-function you might need to adjust your code.

Old code:

const button = new Button({
    onClick() {
        // Do something
        return false;
    },

    listeners : {
        click() {
            console.log('Click');
        }
    }
});

New code:

const button = new Button({
    onClick() {
        // Do something 
    },

    listeners : {
        click() {
            console.log('Click');
        }
    }
});

Panel Changes

The Panel class gained a new wrapper element between the panel's outer element and its content element.

Previously, the markup for a Panel looked like this:

<div>
        <header> ... </header>
        <div>
            <!--...-->
        </div>
</div>

The new markup for a Panel looks like this:

<div>
        <header> ... </header>
        <div>    <!-- NEW ELEMENT -->
            <div>
                <!--...-->
            </div>
        </div>
</div>

Any CSS selectors that target immediate children (>) of panels will need to be refactored.

For example, selectors like this:

.my-custom-panel > .b-panel-content {
    /*...*/
}

Will need to account for the body wrap element:

.my-custom-panel > * > .b-panel-content {
    /*...*/
}

Be aware that there will be additional wrapper elements created if toolbars are set dock to left or right. In such cases, it will be simpler to put the classes on the panel's body element directly using the bodyCls config.

TabPanel Changes

The markup for TabPanel in previous releases looks like the following:

<div>
        <div>
            <div><span> ... </span></div>
            <div><span> ... </span></div>
        </div>
    </div>

There are new elements from the Panel base class and from the Toolbar base class for the new TabBar widget. In addition, the tabs are now a Tab widget which extends Button. This changed the tabs from a <div>/<span> structure into a <button>/<label> structure.

The markup is now more like the following:

<div>

        <!-- New bodyWrap element inherited from Panel: -->
        <div>

            <!-- TabBar is a widget that extends Toolbar: -->
            <div>

                <!-- New scroller overflow tool inherited from Toolbar: -->
                <button />

                <!-- New overflow element containing tabs: -->
                <div>

                    <!-- Tabs are now buttons w/their text in a label element: -->
                    <button><label>General</label></button>
                    <button><label>Predecessors</label></button>
                    <button><label>Successors</label></button>
                </div>

                <!-- New scroller overflow tool: -->
                <button />
            </div>
        </div>
    </div>

CSS encoding

Bryntum now uses Dart SASS to compile CSS from SCSS for the themes. It outputs CSS encoded with UTF-8. The encoding is specified at the top of the CSS file as a @charset tag:

@charset "UTF-8";

It is important that this tag is preserved in the CSS used on page, to guarantee that font icons render as intended.

Minified CSS instead uses a byte order mark to specify encoding, which although invisible to the eye should be preserved in the file used on page.

If you use a custom build process that includes our CSS and icons are not rendered correctly in all browsers, the issue is most likely caused by missing encoding info. Try adding it back or serving the CSS with correct encoding specified in the HTTP header.

DatePicker

The DatePicker header DOM structure was refactored and editOnHover was deprecated as it no longer has any effect on the widget. You can safely remove it from your configuration if using it.

Grid events

The cellSelector param of the Grid cellClick / cellDblClick / cellContextMenu / cellMouseOver / cellMouseOut events was deprecated and will be removed in 5.0

Grid column header content HTML encoding

Grid column header contents are now HTML encoded by default to protect against XSS attacks. You can easily opt out of this behavior by setting htmlEncodeHeaderText to false on your column.

columns : [
    { type : 'name', text : 'Name' },
    { text : 'Responsible<br/>Manager', field : 'manager', width : 100, htmlEncodeHeaderText : false }
]

Framework examples

Frameworks examples were moved to examples/frameworks folder:

Framework Examples folder
Angular examples/frameworks/angular
Ionic examples/frameworks/ionic
React examples/frameworks/react
Vue v2 examples/frameworks/vue
Vue v3 examples/frameworks/vue-3

Grid v4.1.1

Wrapper dependencies

In order to support installing trial package alias for wrappers they now require adding @bryntum/grid dependency to application's package.json file.

Example for licensed version for Angular framework:

{
  "dependencies": {
    "@bryntum/grid": "4.1.1",
    "@bryntum/grid-angular": "4.1.1"
  }
}

Example for trial version for Angular framework:

{
  "dependencies": {
    "@bryntum/grid": "npm:@bryntum/grid-trial@4.1.1",
    "@bryntum/grid-angular": "4.1.1"
  }
}

Grid v4.2.0

MessageDialog changes and new APIs

MessageDialog was refactored and now offers alert, prompt and confirm API methods mimicking the native browser APIs. Each returns a promise which yields the button that was clicked (in the case of prompt, an object with the button and text).

The default Yes / No buttons now say OK and Cancel but can be customized easily:

MessageDialog.confirm({
    title        : 'So tell me what you want, what you really really want?',
    message      : 'A message',
    okButton     : 'Yes',
    cancelButton : 'No'
});
MessageDialog.alert({
    title        : 'Important Message',
    message      : 'Server rebooting in 25 seconds',
    okButton     : 'Got it!'
});
const result = await MessageDialog.prompt({
    title        : 'Enter message',
    message      : 'Please write a message',
    okButton     : 'Save'
});

if (result.button === MessageDialog.okButton) {
    console.log(result.text); // The text entered by user
}

Scroll Manager moved to the Core package

Grid/util/ScrollManager was used by Grid internals to scroll the view in certain conditions. For instance, when moving pointer close to an edge with enableScrollingCloseToEdges set to true, or in Scheduler, when dragging event close to edges. It also assumed it was used for a Grid, or a subclass thereof. When startMonitoring was called with an element, manager would always assume there is a special vertical overflowing element.

In the new release it was moved to Core/util/ScrollManger and untangled from Grid. A new manager does not have any indirect ties, it only monitors what it is told to and can be used for any component.

const manager = new ScrollManager({ element : container });

manager.startMonitoring({
    scrollables : [
        {
            // It supports HTML Element instances
            element : child,
            direction : 'vertical'
        },
        {
            // And selectors
            element : '.selector',
            direction : 'both'
        }
    ]
});

Old API is still supported, but we recommend to stick to the new API:

manager.startMonitoring({
    element : child,
    direction : 'vertical'
})

GlobalEvents no longer exposed on window

GlobalEvents is a singleton that triggers global application level events, such as the theme event when changing themes. Previously it was exposed on window, an approach we are now avoiding to make multiple bundles work better together (since they would otherwise use the last loaded bundles instance instead of their own). If you use it in your application you might have to adjust your code to import it as with all other Bryntum classes.

Old code:

window.GlobalEvents.on({
    theme() {
        // Code that runs when theme is changed
    }
})

New code:

import GlobalEvents from 'lib/Core/GlobalEvents.js';

GlobalEvents.on({
    theme() {
        // Code that runs when theme is changed
    }
})

Grid v4.2.3

PdfExport feature

PdfExport feature's beforeExport event signature has changed, all the export configs are wrapped in a single config object:

Old code:

grid.on({
    beforeExport({ orientation }) { /*...*/ }
})

New code:

grid.on({
    beforeExport({ config }) {
        const { orientation } = config;
        /*...*/
    }
});

PdfExport feature's export event should be listened for on the Grid instance too:

Old code:

grid.feature.on({
    export() { /*...*/ }
})

New code:

grid.on({
    export() { /*...*/ }
})

Grid v4.3.0

Buttons with menus shows a caret down arrow icon

Button has a new config menuIcon that specifies an icon to show when the button has a menu. It defaults to show a caret down arrow icon. If you are using buttons with menus and want to get rid of this icon (for example if you are already displaying a hamburger icon), set menuIcon : null on the button.

Old code:

const button = new Button({
    icon : 'fa fa-bars',
    menu : [
        { text : 'First item'},
        { text : 'Second item'}
    ]
})

New code:

const button = new Button({
    icon     : 'fa fa-bars',
    menuIcon : null,
    menu     : [
        { text : 'First item'},
        { text : 'Second item'}
    ]
})

Buttons menuIconCls deprecated

Buttons menuIconCls was deprecated in favor of the menuIcon config mentioned above. It will be removed in 5.0. Please replace any usages:

Old code:

const button = new Button({
    menuIconCls : 'fa fa-bars',
    menu        : [
        { text : 'First item'},
        { text : 'Second item'}
    ]
})

New code:

const button = new Button({
    menuIcon : 'fa fa-bars',
    menu     : [
        { text : 'First item'},
        { text : 'Second item'}
    ]
})

Grid v4.3.7

PdfExport feature

beforeExport Grid event has been renamed to beforePdfExport and export event has been renamed to pdfExport respectively. Please modify your code to match the new names.

Old code:

grid.on({
    beforeExport({ config }) { /*...*/ },

    export({ config }) { /*...*/ }
})

New code:

grid.on({
    beforePdfExport({ config }) {
        const { orientation } = config;
        /*...*/
    },

    pdfExport({ config }) {
        const { orientation } = config;
        /*...*/
    }
});

Also the events signature had deprecated properties copied from config section which have been removed in this release.

Contents