0

This is not a duplicate post as I've looked and cannot find anything with external status alterations. I've been on and off this topic searching for a few days now.

What I'm doing is the following.

I have the DataTable and a Map on my UI. The DataTable lists the data points that are on the Map.

I can select and multi select on the DataTable, which highlights the row on the data table and interacts with the map and plots highlighted data points just fine. The issue that I'm having is that when I use the map to select the DataTable does not reflect the selected row that goes with that data point on the map UNTIL I mouse over the DataTable which I'm assuming in the background triggers some sort of refresh. I don't have to click or do anything in the DataTable , it just automatically triggers itself and then shows all of the points that I selected within my map.

The variable that the html page binds to for [selected] is being properly called and its value is being set, it just does not reflect the highlights on the rows until the mouse enters the DataTable area. I don't have to click or do anything for it to trigger, it just does so on its own once the mouse enters the area.

Also, I have also tried to manually set the table selected value, e.g.

this.tableRef.selected = this.newRowSelection;

So my question is HOW DO I GET the DataTable to auto display when I make my external selection?

I don't want to DROP the table and Redraw the DataTable as I feel it would not create a good UX.

Appreciate any and all input on this.

=======================================================

Update: Wed 3/11/2020

So after more testing and debugging I can confirm that I am definitely setting the datatable.selected value to the [] of rows that should be selected.

However, again the rows do not highlight until I have moused over the datatable.

Anyone have any suggestions on how to get the datatable to just highlight automatically?

=======================================================

edjm
  • 4,830
  • 7
  • 36
  • 65
  • I found this posting which is similar to my issue but not exact. https://github.com/swimlane/ngx-datatable/issues/1117 – edjm Mar 11 '20 at 19:30
  • Can you please create stackblitz of minimum code required for your need ? – Jasdeep Singh Mar 13 '20 at 14:43
  • I'll have to work on that. I've not used that before to create an app and all of my code is on a different computer system and network which I cannot access from here. I'll need some time to get this done. – edjm Mar 13 '20 at 16:03

3 Answers3

1

Hard to say for sure without a reproducible example, but I think that it's because you are updating the property outside of angular' scope, probably as a result of a click on the map that is not bound that angular way.

You can read about change detection here. Basically, angular triggers change detection for all components its know about, on browser events (mouse click, mouse hover, key events...), ajax calls and setTimeout and setTimeout.

If you update some component property without angular knowing it (e.g. by binding a click to your map withut using nangular's click handler), angular (and your data table) won't know about it and won't refresh until the next change detection (which is the mouse hover for the data table)

So if you make that kind of change outside of angular, you need to tell angular to run change detection again, using one of the 2 methods below.

Wrap your modifications in a setTimeout

setTimeout(()=> this.tableRef.selected = this.newRowSelection);

This will trigger change detection for the whole application

Notify angular explicitely to run change detection again

import {ChangeDetectorRef} from '@angular/core';
constructor(private cdr: ChangeDetectorRef) {}
//....
this.tableRef.selected = this.newRowSelection;
this.cdr.detectChanges();

Note that this will apply only to the component (and children) where you are calling this method from. So if your map and data table are in the same component, this should work straight away. Otherwise, you can propagate some event from the map component to the component containing the table to know when to call this method.

You could also directly execute your action inside angular's zone

import {NgZone} from '@angular/core';
constructor(private ngZone: NgZone) {}
//...
this.ngZone.run(() => this.tableRef.selected = this.newRowSelection);
David
  • 33,444
  • 11
  • 80
  • 118
  • I've tried setting the original stuff I had in a setTimeout and what ends up happening is that selections end up lost when user rapidly selects and deselects from the datatable. I'll give the Zone a try. – edjm Mar 17 '20 at 18:26
  • David, after nearly 3 weeks of this problem it is working. Before I pass along the points, can you please explain how and or why the ngZone.run(...) makes this work because I had tried in prior tests to set the table directly just as you have with the this.tableRef.selected but it would not highlight unless I moused over the table. – edjm Mar 17 '20 at 19:28
  • I updated my answer, but it should normally work with the other methods – David Mar 18 '20 at 07:49
  • Thank you again. I am new to Angular and have little to no support here at work so 1/2 the stuff has been an uphill battle. I would not have found this out on my own. I also appreciate the link to the change detection information. I'll be reading up on that this morning along with the ngZone running. – edjm Mar 18 '20 at 10:57
  • Glad I could help – David Mar 18 '20 at 11:33
1

I have a solution which is not perfect but it works perfectly fine! Simply reassign your rows this.rows = [...this.rows]; before you update the selected row. It somehow triggers the change detection cycle for the datatable.

If you are updating your table rows dynamically, you have to call refreshSelectedRow every time new data is fetched (e.g. in the data update subscription).

If you run the code without the reasignment, it would still work but only for the next rows data update (e.g. from a subscription), where you would highly probably assign a new array object to this.rows anyway. Depending on the frequency of your data update, you would have a delay.

export class MapComponent {
  SelectionType = SelectionType;

  @ViewChild('myTable') table: any;

  @Input() set selectedLotId(value: number | null) {
    this._selected = value;
    this.refreshSelectedRow(); 
  }

  private _selected: number | null;

  rows: Lot[] = [];

  constructor() {}

  this.carParkSub$ = this.store.lots$.subscribe((lots) => {
    this.rows = lots || [];

    // needs to call here as well, cause in my case
    // I am polling new data every 5s 
    this.refreshSelectedRow();
  });

  refreshSelectedRow() {
    if (!this.table) {
      return;
    }
    this.rows = [...this.rows]; //MUST BE RUN!. Otherwise datatable will not pick up changes to selected row;

    let selected = this.rows.find((row) => row.id == this._selected);
    this.table.selected = [];
    if (selected) {
      this.table.selected.push(selected);
    }
  }

}
<ngx-datatable #myTable 
[rows]="rows" 
[selectionType]="SelectionType.single" 
[trackByProp]="'id'">
  table columns and content here
</ngx-datatable>

In my case I use an @Input() prop to set the selected row from the outside and instead of passing a bound prop to the table in the template I use the programmatic way this.table.selected.push(selected);

Note: Keep in mind that I removed the ballast from the code so simple copy&paste will not work.

Patronaut
  • 1,019
  • 10
  • 14
  • 1
    Funny thing. I just ran across information about the 'this.rows = [...myNewValue]' last week. Previously I had used the this.ngZone.run() which worked but found out that the use of the ... will trigger the datatable to recognize the change and also will highlight the selections. So the simple answer is to use this.rows = [...this.rows] and not this.rows = myNewValue; – edjm Dec 10 '20 at 12:21
0

I tried various solutions including ChangeDetectorRef, NgZone and updating reference by [...rows]. None worked. The selection logged for table was always correct and the checkbox was never visually checked.

What worked for me was the solution here

<ngx-datatable
    ...
    [rowIdentity]="getId">


getId(row) {    
    return row.id;
}

This will help the datatable identify the selected rows correctly.

Boat
  • 402
  • 4
  • 14