Our blazing fast Grid component built with pure JavaScript


Post by himanshurjoshi »

Hi,
I am having a data structure which have three levels of data like, Sales Order that contain multiple Orders, and Orders contains multiple Invoices. For example

[{
    "OrderDate": "02/09/2024",
    "Description": "MANUAL PO 1",
    "POSalesOrder": "12345",
    "CostCode": "",
    "BudgetedAmount": 0,
    "TotalAmount": 200,
    "AmountPaid": 50,
    "BalanceDue": 150,
    "Status": null,
    "OrdersDetail": [
        {
            "header": "MANUAL PO 1",
            "OrderTotalAmount": 200,
            "OrderBalanceDue": 150,
            "IsManual": true,
            "Deliveries": [
                {
                    "DeliveryInvoice": "1",
                    "InvoiceDate": "02/09/2024",
                    "Status": null,
                    "Description": "",
                    "InvoiceType": null,
                    "DeliveryDate": null,
                    "DueDate": "02/09/2024",
                    "AmountInvoiced": 200,
                    "AmountPaid": 50,
                    "BalanceDue": 150,
                    "url": null,
                    "CostCode": "",
                    "QBBillResponseId": 0,
                    "QBBillResponseCreateTime": null,
                    "InvoiceId": 14,
                    "isExpanded": false,
                    "ExpenseId": 72
                }
            ],
            "OrderDetailId": 70,
            "status": null,
            "isExpanded": false,
            "is_new_row": false,
            "ExpenseId": 72
        }
    ],
    "QB_Configured": false,
    "IsManual": true,
    "Vendor": "Vendor 1",
    "ExpenseId": 72,
    "isExpanded": false,
    "created_at": "2024-02-09T09:27:55.302707+00:00"
}]

So this will look like this

ss grid.png
ss grid.png (75.42 KiB) Viewed 381 times

I am following this example but not able to achieve the goal
https://bryntum.com/products/grid/examples/nested-grid/

Can you please provide me the sample code for this?


Post by marcio »

Hey himanshurjoshi,

What issues are you having when you try to use the demo that you mentioned?

Looks really close to what you want to achieve, and you have access to the source code in the live demo.

Best regards,
Márcio


Post by himanshurjoshi »

I am using typescript, and it is not supporting relation name as variable as mentioned here

class Employee extends GridRowModel {
    static fields = ['id', 'firstName', 'name', 'start', '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 [b]this.timeRows[/b].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 : '[b]timeRows[/b]' // The field in which these records will be accessible from the "other" record
    }
};
}

this.<Relation Name> is not supported in typescript that's why I can not create get property which is based on calculation of child row fields. Can you suggest me how to achieve this in typescript?

I need this calculated property because I need to update Totals in parent row if there any changes in child rows. If I directly update values in parent row then Grid retendered and the expanded row get collapsed so I have to expand it programmatically which is annoying for user.


Post by alex.l »

Hi, please use //ts-ignore instruction for now, that's a typings problem. I've opened a ticket here https://github.com/bryntum/support/issues/8530

It should be working well, it's just typings that auto-generated and we need to improve that.

All the best,
Alex


Post by himanshurjoshi »

I used //@ts-ignore and compilation error ignored now its giving run time error

react-dom.production.min.js:189 ReferenceError: orderRows is not defined
at get TotalAmount (GridConfig.tsx:82:5)
at ModelClass.getValue (grid.module.js:19420:1)
at NumberColumn.getRawValue (grid.module.js:75745:1)

My model classes and data like this

const po = [
  {
    id: 1,
    OrderDate: '02/09/2024',
    Description: 'MANUAL PO 1',
    POSalesOrder: '12345',
    CostCode: '',
    BudgetedAmount: 0,
    TotalAmount: 200,
    AmountPaid: 50,
    BalanceDue: 150,
    Status: null,
    QB_Configured: false,
    IsManual: true,
    Vendor: 'Vendor 1',
    ExpenseId: 72,
    isExpanded: false,
    created_at: '2024-02-09T09:27:55.302707+00:00',
  },
];

const ordersDetail = [
  {
    header: 'MANUAL PO 1',
    OrderTotalAmount: 200,
    OrderBalanceDue: 150,
    IsManual: true,
    OrderDetailId: 70,
    status: null,
    isExpanded: false,
    is_new_row: false,
    ExpenseId: 72,
  },
];

const deliveries = [
  {
    DeliveryInvoice: '1',
    InvoiceDate: '02/09/2024',
    Status: null,
    Description: '',
    InvoiceType: null,
    DeliveryDate: null,
    DueDate: '02/09/2024',
    AmountInvoiced: 200,
    AmountPaid: 50,
    BalanceDue: 150,
    url: null,
    CostCode: '',
    QBBillResponseId: 0,
    QBBillResponseCreateTime: null,
    InvoiceId: 14,
    isExpanded: false,
    ExpenseId: 72,
  },
];
class POSalesOrder extends Model {
  static get fields() {
    return [
      'id',
      'OrderDate',
      'Vendor',
      'Description',
      'POSalesOrder',
      'CostCode',
      'BudgetedAmount',
      'AmountPaid',
      'BalanceDue',
      'Status',
    ];
  }

  get TotalAmount() {
    //@ts-ignore
    return orderRows.reduce((acc: any, r: any) => acc + r.OrderTotalAmount, 0);  //Run time error at this line
  }
}

class OrdersDetail extends Model {
  static get fields() {
    return [
      'header',
      'OrderBalanceDue',
      'IsManual',
      'OrderDetailId',
      'status',
      'isExpanded',
      'is_new_row',
      'ExpenseId',
    ];
  }

  get OrderTotalAmount() {
    //@ts-ignore
    return deliveryRows.reduce((acc: any, r: any) => acc + r.AmountInvoiced, 0);
  }

  static relations = {
    orderDetail: {
      foreignKey: 'ExpenseId',
      foreignStore: 'poSalesOrderStore',
      relatedCollectionName: 'orderRows',
    },
  };
}

class Deliveries extends Model {
  static get fields() {
    return [
      'DeliveryInvoice',
      'InvoiceDate',
      'Status',
      'Description',
      'InvoiceType',
      'DeliveryDate',
      'DueDate',
      'AmountInvoiced',
      'AmountPaid',
      'BalanceDue',
      'url',
      'CostCode',
      'QBBillResponseId',
      'QBBillResponseCreateTime',
      'InvoiceId',
      'isExpanded',
      'ExpenseId',
    ];
  }

  static relations = {
    orderDetail: {
      foreignKey: 'ExpenseId',
      foreignStore: 'ordersDetailStore',
      relatedCollectionName: 'deliveryRows',
    },
  };
}

const poSalesOrderStore = new Store({
  modelClass: POSalesOrder,
  data: po,
});

const ordersDetailStore = new Store({
  modelClass: OrdersDetail,
  data: ordersDetail,
});

Post by tasnim »

Hi,

Is it possible to share a runnable sample test case with us so we can reproduce the issue and debug it? And give you a proper solution?
A test case would make the assistance much faster


Post by himanshurjoshi »

Here is the code

sample code.zip
(334.09 KiB) Downloaded 24 times

Post by tasnim »

Hi,

While running your application faced some errors, after tweaking a bit I was able to run your application but it has styles missing.

Screenshot 2024-02-13 173422.png
Screenshot 2024-02-13 173422.png (15.14 KiB) Viewed 290 times

Is there anything I missed running your application? Could you please share an app that has all the styles included and the steps to run the app?


Post by himanshurjoshi »

.custom_budget .b-grid-row.pavel {
  background-color: #eeeeee;
}

.custom-grid-inner-body .b-grid-header,
.custom-grid-inner-body .b-grid-cell,
.custom_budget .b-grid-header {
  border: none !important;
}

.custom-grid-inner-body .b-grid-row,
.custom-grid-inner-body .b-grid-headers {
  border-inline-start: none !important;
}

.custom_budget .b-grid-cell {
  border: none !important;
  background-color: #f1f0ef !important;
}

.custom_budget .b-grid-row {
  border-bottom: 1px solid #c2c1c1 !important;
}

.custom_budget .b-grid-header-container {
  background-color: white !important;
  z-index: 4;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
}

.custom_budget .b-grid-header {
  text-transform: none !important;
}

.custom_budget .text-info {
  display: grid;
  grid-template-columns: 0.2fr 0.2fr;
  row-gap: 0.5em;
  margin-bottom: 1em;
}

.custom_budget .b-rowexpander-body .expander-content {
  flex: 1 !important;
  padding-inline-start: 2em !important;
  font-size: small;
}

.custom_budget .b-rowexpander-body {
  display: flex !important;
  border-top: none !important;
  left: 0 !important;
  right: 0 !important;
  color: #606263;
}

.custom_budget
  .b-gridbase:not(.b-animating)
  .b-grid-row:not(.b-grid-row-updating)
  * {
  display: grid;
}

.custom_budget
  .b-gridbase:not(.b-animating)
  .b-grid-row:not(.b-grid-row-updating)
  .cost_code_cell
  .b-field-inner {
  display: flex !important;
}

.custom_budget .splits-grid {
  display: grid;
  grid-template-columns: 0.2fr 0.2fr 0.18fr 0.18fr 0.18fr 0.2fr 0.16fr 0.18fr 0.18fr 0.18fr 0.18fr 0.18fr;
  width: 100%;
  font-size: small;
}

.custom_budget .b-rowexpander-body {
  padding: 20px 0;
  background-color: #e9f2fa;
}

.custom_budget .b-rowexpander-body .splits-grid .header {
  background-color: #dcebf9;
}

.custom_budget .b-rowexpander-body .splits-grid > div {
  border-bottom: 1px solid #ccc;
  align-items: center;
  height: 30px;
}

.custom_budget .b-rowexpander-row-expanded {
  background-color: #dcebf9;
}

.custom_budget .b-rowexpander-body .text-info {
  padding-top: 20px;
}

.custom_budget .link {
  color: blue;
  cursor: pointer;
}

.custom_budget .disabled-link {
  color: gray;
}

.custom_budget .buttons {
  user-select: none;
}

.custom_budget .buttons > div {
  cursor: pointer;
  color: #2667c8;
}

.hide {
  display: none !important;
}
.b-toolbar.b-dock-top .b-toolbar-content,
.b-toolbar.b-dock-bottom .b-toolbar-content {
  min-height: var(--toolbar-min-size);
  font-size: small;
}

.custom_budget .b-rowexpander-row-expanded .b-grid-cell {
  background-color: #cbe5fd !important;
  font-weight: 700;
}

.custom-grid-outer-body {
  margin: 0 !important;
  padding: 0px 20px 20px 20px !important;
  background-color: #e9f2fa !important;
  border: none !important;
}

.custom-grid-outer-body .b-grid-row {
  background-color: transparent !important;
  border: none !important;
  color: #368eff !important;
}

.custom-grid-inner-body {
  background-color: #e9f2fa !important;
  margin: 0 !important;
  padding: 15px !important;
}

.custom-grid-inner-body .b-grid-headers .b-grid-header {
  background-color: #cbe5fd !important;
}

.custom-grid-inner-body .b-grid-header-container {
  box-shadow: none !important;
}

.custom-grid-inner-body .b-grid-headers .b-grid-header .b-grid-header-text {
  font-weight: normal !important;
}

.custom-grid-inner-body .b-grid-row .b-grid-cell:last-child .b-widget.b-button {
  background-color: transparent !important;
  border: none !important;
  color: #368eff !important;
}

.b-gridbase:not(.b-masked) .b-grid-empty .b-empty-text {
  display: block;
  color: #667280;
  background-color: #e9f2fa;
  padding: 1em;
  position: absolute;
  pointer-events: none;
  width: 100%;
}

.custom_budget
  .b-rowexpander-row-expanded
  .b-grid-cell
  .b-pickerfield
  .b-field-inner,
.custom_budget .b-grid-cell .b-pickerfield .b-field-inner {
  display: flex !important;
}

.b-container.b-outer
  .b-vbox.b-grid-body-wrap
  .b-dock-top.b-top-toolbar
  .b-toolbar-content,
.b-container.b-outer
  .b-vbox.b-grid-body-wrap
  .b-dock-bottom.b-top-toolbar
  .b-toolbar-content {
  min-height: var(--toolbar-min-size);
  font-size: small;
  background-color: #e9f2fa;
}

.b-container.b-outer .b-vbox.b-grid-body-wrap .b-dock-top.b-top-toolbar {
  border-bottom: none !important;
}

.b-rowexpander-row-expanded .b-rowexpander-body {
  border-top: none !important;
}

Post by tasnim »

Hi,

I've applied these styles to your app. but still it's showing the same. Attaching your edited app here, you can also check it by running it.
Please provide a runnable app with the steps demonstrating how to run it!

Attachments
sample code edited.zip
(28.73 KiB) Downloaded 16 times

Post Reply