ngx-datatable: Async Problem when using @Input as data source

I’m submitting a … (check one with “x”)

[ x ] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here

Current behavior

After upgrading to 1.1 I am getting the error error_handler.js:47 EXCEPTION: Error in ./DataTableBodyComponent class DataTableBodyComponent - inline template:14:8 caused by: Cannot read property ‘length’ of null

This happens when I pass the data via Input from another component.

My dumb component has: <datatable class=“material” [rows]=“rowData” [columns]=“columns” [columnMode]=“‘force’” [headerHeight]=“50” [footerHeight]=“50” [rowHeight]=“‘auto’”> </datatable>

where “rowData” is passed via @Input @Input() rowData: Array<Workaround> = [];

I can see my data in the component if I output it {{ rowData | json }} (I should mention that I am using ngrx and that I just updated to angular 2.2.0-rc.0

Expected behavior

It should display the data correctly as it did on v 0.11

Reproduction of the problem

Reproducible plunkr http://plnkr.co/edit/PrGJLIVC3FMGa3BqOTxw?p=preview

What is the motivation / use case for changing the behavior?

Please tell us about your environment:

Mac OS, angular-cli beta 19.3

  • Table version: 1.1
  • Angular version: 2.2.0-rc.0
  • Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
  • Language: [all | TypeScript X.X | ES6/7 | ES5]

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 37 (14 by maintainers)

Most upvoted comments

This PR should take care of the mutations that were causing store freeze to not work. https://github.com/swimlane/ngx-datatable/pull/623

It will be landing with v8.0.0 this week!

I’ll share my workaround:

In my scenario, I have a smart component that calls a dumb component to render the datatable

Smart component:

...
export class EngneedsListComponent implements OnInit {

  searchQuery$: Observable<string>;
  rowData$: Observable<EngineeringNeed[]>;
  rowData: Array<EngineeringNeed>; // need this extra var to deep copy the data
  loading$: Observable<boolean>;
  itemRoute: string = '';

  constructor( private router: Router,
               private store: Store<fromRoot.State>,
               private service: EngneedsService ) {
    this.searchQuery$ = store.let(fromRoot.getEngNeedSearchQuery).take(1);
    this.rowData$ = store.let(fromRoot.getEngNeedSearchResults);
    this.loading$ = store.let(fromRoot.getEngNeedSearchLoading);
  }

  ngOnInit() {
   // I need this extra step so I can pass this.rowData (deep copy) to the dumb component
    this.rowData$.subscribe(engNeeds => {
      // prevent datatables from mutating the data
      this.rowData = JSON.parse(JSON.stringify(<EngineeringNeed[]>engNeeds)); 
    });
    this.store.dispatch(new action.LoadAllAction());
  }
}

In the template:

          <aui-engneed-table-list
            [rowData]="rowData" // note in here I could not do [rowData]="rowData$ | async"
            (onEdit)="onEdit($event)"
            (onDelete)="onDelete($event)"></aui-engneed-table-list>

In the dumb component: (aui-engneed-table-list)

... 
@Input() rowData: Array<EngineeringNeed> = [];
...
        <datatable
          class='material datatable fixed-header fixed-row scroll-vertical'
          [rows]='rowData'
          [columnMode]="'force'"
          [headerHeight]="50"
          [footerHeight]="0"
          [rowHeight]="50"
          [scrollbarV]="true"
          [scrollbarH]="true">
         .... my columns here ...
</datatable>

@amcdnl The issue has to do with data mutation of the passed rows object. I’ll try to come up with a plunker but it requires quite a bit of work to reproduce it exactly.

Note that if we disable storeFreeze passing [rowData]=“rowData$ | async” works without the deep copy workaround, but once enabled storeFreeze will throw “Can’t add property $$index, object is not extensible” Also: as @ethanve mentioned. Even with the workaround the data takes a while to show or it does not show until you click anywhere or resize the screen so I am disabling storeFreeze for the time being.

So I feel datatables should not touch the data coming in as a final solution. StoreFreeze is supposed to help you during development to stay true to the redux pattern.

Should we reopen this issue and track the right status?

I use the latest version 9.2.0 with Redux (ngrx) and ngrx-store-freeze module. I run into the same issue. It is easy producible since the original JSON object has been modified with index$$: ... and other keys/values.

Is there a timeline on when this feature will be landed? Is there a hack that I can use without removing freeze store function check?

Sadly, I had to back this change out due to regressions. I’ll be on vaca next week but this is on my list as I’m moving to ngrx-store soon.

I think it would be great if we kept the row data immutable and have some other internal array holding on to the meta data (such as the $$index)?

I am not sure what was fixed but this continues to be an issue with ngrx store freeze. I am forced to pass a copy of the data to prevent mutation.

Revisiting this after a while I also noticed the following error when I enabled storefreeze in ngRx

error_handler.js:47 EXCEPTION: Error in ./DatatableComponent class DatatableComponent - inline template:25:8 caused by: Can’t add property $$index, object is not extensible

This is because Datatables is mutating the data being passed to it. Perhaps this could also be addressed by the the component as well.

My workaround for the time being is:

  store.let(getIssueRelatedWorkarounds()).subscribe(workarounds => {

        // prevent datatables from mutating the data
        this.rowData = JSON.parse(JSON.stringify(<Workaround[]>workarounds));

  });