Our blazing fast Grid component built with pure JavaScript


Post by bensullivan »

Hi

I have an issue where I'm trying to update the cell of a column that has been dynamically added. When the cell loses focus, the new value is not displayed, although when I go back in the cell appears to have the value that was set before.

Here is my code:
import {Grid, WidgetHelper, Store} from './build/grid.module.js';

const personStore = new Store({
    idField: 'value',
    data : [
        { text : 'Select...' },
        { value: 'PERS-1', text : 'Bilbo Baggins' },
        { value: 'PERS-2', text : 'Samwise Gamjee' },
        { value: 'PERS-3', text : 'Peregrin Took' }
    ]  
 });

const grid = new Grid({
    appendTo: 'target',
    features : {
        cellEdit: true,
        stripe: true
    },
    showDirty: true,
    columns: [
        { 
            field: 'person', 
            text: 'Person', 
            editor : { type : 'dropdown', store : personStore },
            renderer({ record }) {
                const person = personStore.getById(record.person);
                return person ? person.text : record.person;
            }, 
            width: 300, 
            locked: true 
        },
        { field: 'role', text: 'Role', width: 150, locked: true },
        { field: 'team', text: 'Team', width: 150, locked: true },
        { text: '2019<br>Nov', type: 'percent', field: 'capacity1', width: 60}
    ],
    data : [
        {person: 'Select...', role: 'Select...', team: 'Select...', capacity1: 0}
    ]
});

WidgetHelper.append([
    {
        type     : 'button',
        ref      : 'removeButton',
        text     : 'Remove Last Month',
        color    : 'b-orange b-raised',
        icon     : 'b-fa b-fa-minus',
        tooltip  : 'Remove Last Month',
        onAction : () => grid.columns.count > 1 && grid.columns.last.remove()
    },
    {
        type     : 'button',
        ref      : 'addButton',
        text     : 'Add Month',
        color    : 'b-green b-raised',
        icon     : 'b-fa b-fa-plus',
        tooltip  : 'Add new month',
        onAction : () => {
            grid.columns.add({ text: 'Dec', type: 'percent', field: 'capacity2', width: 60});
        }
    }  
], { insertFirst : document.getElementById('gridButtons') || document.body });
Any ideas on what I'm doing wrong?

Thanks!

Ben

Post by sergey.maltsev »

Hi!

While Grid doesn't "know" anything about capacity2 data format then it can't handle it correctly.
You should either add capacity2 to initial data
data : [
        {person: 'Select...', role: 'Select...', team: 'Select...', capacity1: 0, capacity2: 0}
    ]
Or better extend GridRowModel to use your data fields and set it as modelClass to Grid store.
Something like this code.
class MyRowModel extends GridRowModel {
    static get fields() {
        return [
            'person',
            'role',
            'team',
            { name : 'capacity1', type : 'number' },
            { name : 'capacity2', type : 'number' }
        ];
    }
}

const
    grid = new Grid({
        appendTo : 'container',
        features : {
            cellEdit : true,
            stripe   : true
        },
        showDirty : true,
        columns   : [
            {
                field  : 'person',
                text   : 'Person',
                editor : { type : 'dropdown', store : personStore },
                renderer({ record }) {
                    const person = personStore.getById(record.person);
                    return person ? person.text : record.person;
                },
                width  : 300,
                locked : true
            },
            { field : 'role', text : 'Role', width : 150, locked : true },
            { field : 'team', text : 'Team', width : 150, locked : true },
            { text : '2019<br>Nov', type : 'percent', field : 'capacity1', width : 60 }
        ],
        store : new Store({
            modelClass : MyRowModel,
            data       : [{ person : 'Select...', role : 'Select...', team : 'Select...' }]
        })
    });

Model usage info can be found here
https://www.bryntum.com/docs/grid/#Common/data/Model

Store model class
https://www.bryntum.com/docs/grid/#Common/data/Store#config-modelClass

Post by bensullivan »

Hi Sergey

Thanks for the ideas. I'm wondering if what you suggest will work if we need to dynamically add and remove new columns to the grid which need to be editable.

Your first idea I'm no sure will work for us because we don't know how many columns we'll need until runtime - this user needs to be able to decide how many months columns they need.

I don't understand how MyRowModel, which looks like it's fields() method is statically defined to have capacity1 and capacity2, can permit the modifying of column fields at runtime?

What am I missing?

Thanks very much for your help

Ben

Post by bensullivan »

Would it be possible to use the prototype of MyRowModel to replace the fields() function with something that returns an array with the new field?

Post by sergey.maltsev »

Hi, bensullivan!

Currently we have no support for dynamic updating of store fields which is required to create a new column for non-existing field.

I've created a feature request ticket for this
https://app.assembla.com/spaces/bryntum/tickets/9328-add-ability-to-modify-store-fields-dynamically/details

As a temporary workaround take a look at this sample app code which shows how to recreate a store with new fields set.
It adds a column and new row with new value for the created column.
import '../_shared/shared.js'; // not required, our example styling etc.
import Grid from '../../lib/Grid/view/Grid.js';
import '../../lib/Grid/column/NumberColumn.js';
import Store from '../../lib/Common/data/Store.js';
import '../../lib/Grid/column/PercentColumn.js';
import WidgetHelper from '../../lib/Common/helper/WidgetHelper.js';

let column = 1;

const myFields = [
    'person',
    'role',
    'team'
];

const
    grid = new Grid({
        appendTo : 'container',
        features : {
            cellEdit : true,
            stripe   : true
        },
        showDirty : true,
        columns   : [
            { field : 'role', text : 'Role', width : 150, locked : true },
            { field : 'team', text : 'Team', width : 150 }
        ],
        store : new Store({
            fields : myFields,
            data   : []
        })
    });

WidgetHelper.append([

    {
        type     : 'button',
        text     : 'Add column',
        color    : 'b-green b-raised',
        icon     : 'b-fa b-fa-plus',
        onAction : () => {

	   // Creating new field for myFields
            const fieldName = `column${column++}`;
            myFields.push({ name : fieldName, type : 'number' });

           // Creating new Store for updated fields with existing store data copy
           const store = new Store({
                fields : myFields,
                data   : grid.store.records.map(r => r.data)
            });

            grid.store = store;
            grid.refreshRows();
            
            // Adding newly created column
            grid.columns.add({ text : fieldName, type : 'percent', field : fieldName, width : 100 });

            // Just for example adding new row record with new field.
            // Id is set to avoid be the same with generated one for existing records
            grid.store.add({ id : grid.store.count + 1, [fieldName] : column });
        }
    }
], { insertFirst : document.getElementById('tools') || document.body });

Post by bensullivan »

Hi Sergey

Thanks so much for your help. I'll give this a try tomorrow morning. Do you have any idea on when work might start on this feature request?

Thanks

Ben

Post by sergey.maltsev »

Hi!

We are going to start work on this feature today.
I'm not sure when it would be finished but you can check ticket status for more information.

Post by bensullivan »

That's awesome! Thanks so much!

Post by bensullivan »

Hi

I noticed this work had been completed - do you have any idea on when it will become available?

Thanks

Ben

Post by sergey.maltsev »

Hi!

This will be available in next release.
After the task is marked resolved It should be available in nightly builds suffixed with -next.

Post Reply