revolist / revogrid

Powerful virtual data grid smartsheet with advanced customization. Best features from excel plus incredible performance 🔋
https://rv-grid.com
MIT License
2.72k stars 171 forks source link

ERROR TypeError: Cannot assign to read only property 'columnName' of object '[object Object]' #554

Closed Thoemmy closed 1 week ago

Thoemmy commented 1 week ago

Describe the issue

My config for the angular app is looking as following, if i load the dev environment setup with production = false then i receive an error when i try to change some values in the revogrid table, but if production=true then i do not receive any errors.

By mistake, the production mode was permanently switched on, which also meant that the strict checks by angular were not active.

I tried to add a "cloned" array to the source of revogrid but same situation (used spread operator), i did not tried it with loadash cloneDeep cause it should be possible without i guess.

Would be great if you could give me some suggestions which i can check or test.

Angular Config:

import { ApplicationConfig, enableProdMode, importProvidersFrom, isDevMode } from '@angular/core';
import { provideRouter, withDisabledInitialNavigation, withEnabledBlockingInitialNavigation } from '@angular/router';
import { appRoutes } from './app.routes';
import { BrowserUtils } from '@azure/msal-browser';
import { environment } from '../environments/environment';
import { BrowserModule } from '@angular/platform-browser';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations';
import { provideStore } from '@ngrx/store';
import { provideRouterStore, routerReducer } from '@ngrx/router-store';
import { provideStoreDevtools } from '@ngrx/store-devtools';

const initialNavigation =
  !BrowserUtils.isInIframe() && !BrowserUtils.isInPopup()
    ? withEnabledBlockingInitialNavigation()
    : withDisabledInitialNavigation();

// This line ################
if (environment.production) {
  enableProdMode();
}
// ##########################

export const appConfig: ApplicationConfig = {
  providers: [
    importProvidersFrom(BrowserModule),
    provideRouter(appRoutes, initialNavigation),
    provideAnimations(),
    provideRouterStore(),
    provideStore({
      router: routerReducer,
    }),
    provideStoreDevtools({
      maxAge: 25,
      logOnly: !isDevMode(), 
      autoPause: true, 
      trace: true,
      traceLimit: 75,
      connectInZone: true,
    }),
    provideHttpClient(withInterceptorsFromDi()),
  ],
};

Error message after editing a cell in revogrid:

VM1700:1  ERROR TypeError: Cannot assign to read only property 'columnName' of object '[object Object]'
    at DataProvider.setCellData (revo-grid.js:703:20)
    at revo-grid.js:3723:31
    at Generator.next (<anonymous>)
    at asyncGeneratorStep (asyncToGenerator.js:3:1)
    at _next (asyncToGenerator.js:22:1)
    at _ZoneDelegate.invoke (zone.js:365:28)
    at Object.onInvoke (core.mjs:14882:33)
    at _ZoneDelegate.invoke (zone.js:364:34)
    at ZoneImpl.run (zone.js:111:43)
    at zone.js:2447:40
    at _ZoneDelegate.invokeTask (zone.js:398:33)
    at core.mjs:14556:55
    at AsyncStackTaggingZoneSpec.onInvokeTask (core.mjs:14556:36)
    at _ZoneDelegate.invokeTask (zone.js:397:38)
    at Object.onInvokeTask (core.mjs:14869:33)
    at _ZoneDelegate.invokeTask (zone.js:397:38)
    at ZoneImpl.runTask (zone.js:158:47)
    at drainMicroTaskQueue (zone.js:577:35)
    at invokeTask (zone.js:483:21)
    at ZoneTask.invoke (zone.js:468:48)

If i enable prod mode this error do not occur, this is really strange.

Thoemmy commented 1 week ago

My current workaround is as following:

Custom Revogrid comp wrapper

  ngOnChanges(changes: SimpleChanges): void {
    this.newSource = _.cloneDeep(changes['source'].currentValue);
  }

  ngOnInit(): void {
    this.newSource = _.cloneDeep(this.source);
  }

But i think there must be a other way ...

m2a2x commented 1 week ago

I'll check soon, ty @Thoemmy !

m2a2x commented 1 week ago

Hi @Thoemmy,

Thank you for sharing your example. After reviewing it (next time better provide github repo to clone, easier to catch), I noticed that the issue seems to stem from the following line:

this.newSource = _.cloneDeep(changes['source'].currentValue);

It looks like changes['source'].currentValue is a readonly value, which is likely an @Input() property. Attempting to modify a readonly value directly (or even indirectly via deep cloning) breaks Angular's reactivity system. For example, trying something like changes['source'].currentValue[0] = {} would result in the same error because @Input() properties should not be modified directly.

There are several ways to handle this:

  1. I highly recommend reviewing the grid's data synchronization documentation: Data Sync Guide. This will give you a clearer understanding of how to properly manage the grid's data.

  2. Decide on Data Editing Strategy: Based on the approach you choose after reading the documentation, you’ll need to decide whether:

    • The grid is allowed to modify your data directly, or
    • You will manage the data externally and only pass updates to the grid.
  3. Avoid cloneDeep: Instead of using _.cloneDeep, you can use a simple array spread to create a new reference without deep cloning:

    this.newSource = [...changes['source'].currentValue];

    This ensures that newSource is updated, but keep in mind that this will not affect the original source array, meaning changes will only apply to newSource and not the original data.

Let me know if you need further clarification or assistance with implementing this approach!

Cheers.