Skip to content

Bryntum nested grid store doesn't catch changes when add record #12673

Description

@chuckn0rris

Forum post

In the Bryntum nested grid, I need to understand how we can detect whether the store changes contain modified records or newly added records.

For example, when we edit a record in the nested grid and then check the store, it correctly contains the modified record:

const { store } = source.up('grid');

const storeChanges = await store.changes,
modifiedRows = storeChanges.modified;
However, in the case of newly added records, when we check store.changes, it returns null. As a result, we cannot retrieve the added records:

addedRows = storeChanges.added;
https://bryntum.com/products/grid/examples/nested-grid/

When we edit a record in the nested grid and then click Save, the store correctly contains the changes, and modified.count returns the number of modified records.

import shared from '../_shared/shared.module.js?491841';
import { DataGenerator, RandomGenerator, DateHelper, Store, StringHelper, GridRowModel, Grid } from '../../build/grid.module.js?491841';
//region "lib/data.js"

const randomGenerator = new RandomGenerator();

const getEmployees = () => {
    const data = DataGenerator.generateData(3);

for (const row of data) {
    DateHelper.add(row.start, row.id * 2, 'month', false);
    row.email = `${row.name.toLowerCase().replaceAll(' ', '.')}@example.com`;
}
return data;
};

const getTimeRows = (employees) => {
    const
        tasks = DataGenerator.generateEvents({ viewStartDate : new Date(), viewEndDate : new Date(), nbrResources : 1, nbrEvents : 35 }).events.map(e => e.name),
        rows  = [];
    let id = 0;

for (const employee of employees) {
    for (let i = 0; i <  Math.ceil(Math.random() * 10); i++) {
        id += 1;
        rows.push({
            id,
            employeeId : employee.id,
            name       : randomGenerator.fromArray(tasks),
            hours      : Math.ceil(Math.random() * 100),
            attested   : Math.random() > 0.5
        });
    }
}
return rows;
};

//endregion

// These two model classes and two stores use the built-in data-relations feature to connect related records to each other.
class Employee extends GridRowModel {
    static fields = [
        'id',
        'firstName',
        'name',
        { name : 'start', type : 'date' },
        'email'
    ];

// Example how to set up a "calculated" field
get unattested() {
    return this.timeRows?.reduce((acc, r) => acc + (r.attested ? 0 : 1), 0);
}

// Example how to set up a "calculated" field
get totalTime() {
    return this.timeRows?.reduce((acc, r) => acc + r.hours, 0);
}
}

class TimeRow extends GridRowModel {
    static fields = ['id', 'employeeId', 'project', 'hours', 'attested'];

// The relations config, will set up the relationship between the two stores and model classes
static relations = {
    employee : {
        foreignKey             : 'employeeId', // The id of the "other" record
        foreignStore           : 'employeeStore', // Must be set on the record's store
        relatedCollectionName  : 'timeRows', // The field in which these records will be accessible from the "other" record
        propagateRecordChanges : true // This makes the outer Grid react to the expanded Grid(s) changes
    }
};
}


const employeeStore = new Store({
    modelClass : Employee,
    data       : getEmployees()
});

const timeRowStore = new Store({
    modelClass : TimeRow,
    employeeStore, // Must be set, see relations config above
    data       : getTimeRows(employeeStore.records)
});

const grid = new Grid({
    appendTo : 'container',

features : {
    rowExpander : {
        // The widget config declares what type of Widget will be created on each expand
        widget : {
            cls        : 'timerow-grid', // CSS class added to the outer element
            type       : 'grid',
            autoHeight : true, // Grid resizes to fit all rows
            columns    : [
                { text : 'Project', field : 'name', flex : 1 },
                { text : 'Hours spent', field : 'hours', width : 135, type : 'number', align : 'end' },
                { text : 'Attested', field : 'attested', width : 110, type : 'check' }
            ],
            // Bottom toolbar
            tbar : [
                '->',
                // Add button, which adds a row to the expanded grid and starts editing it
                {
                    text      : 'Add',
                    icon      : 'b-icon-add',
                    rendition : 'text',
                    onClick   : ({ source }) => {
                        const
                            grid           = source.up('grid'),
                            expandedRecord = grid.owner.features.rowExpander.getExpandedRecord(grid),
                            [newRecord]    = grid.store.add({ name : null, hours : 0, employeeId : expandedRecord.id });
debugger;
// store.changes.added.count
                                
                    }
                },
                {
                    // Button that sets all rows as "attested"
                    text      : 'Save button',
                    icon      : 'fa fa-save',
                    rendition : 'text',
                    onClick   : async({ source }) => {
                        const { store } = source.up('grid');
debugger;
                            var storeChanges = await store.changes,
                            modifiedRows = storeChanges.modified,
							addedRows = storeChanges.added;

                    }
                }
            ]
        },
        dataField : 'timeRows' // Tells rowExpander widget to populate it's store from this field
    }
},

columns : [
    { text : 'Employee#', field : 'id', width : 130, align : 'center' },
    { text : 'Name', field : 'name', flex : 1 },
    { text : 'Start date', field : 'start', flex : 1, type : 'date' },
    {
        text       : 'Email',
        field      : 'email',
        flex       : 1,
        htmlEncode : false, // To be able to use HTML in the renderer
        renderer   : ({ value }) => StringHelper.xss`<i class="fa fa-envelope"></i><a href="mailto:${value}">${value}</a>`
    }, {
        text   : 'Attested',
        field  : 'unattested',
        width  : 110,
        editor : false,
        renderer({ value, cellElement }) {
            cellElement.style.color = value ? '#e53f2c' : '#4caf50';
            return value ? value + ' left' : 'All done';
        }
    },
    { text : 'Total time', field : 'totalTime', width : 120, align : 'center', editor : false }
],

store : employeeStore,

listeners : {
    renderRows : ({ source }) => {
        // This will expand third row at a state where the theme has loaded
       const rowExpander = source.features.rowExpander;
			source.store.forEach(record => {
				rowExpander.expand(record);
			});
    },
    once : true
}
});


Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

OEMOEM customerbugSomething isn't workingforumIssues from forumhigh-priorityUrgent to have fixed

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions