question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Row detail view + row draggable creates weird behavior

See original GitHub issue

IMGUR LINK TO DEMO: https://imgur.com/a/lUTxqKV

Your Environment

Software Version(s)
Angular 8.0.3
Angular-Slickgrid 2.9.9

Context

Trying to create a grid with draggable rows for rearranging + rows with detailed view.

Expected Behavior

When rows are expanded to detailed view and dragged to be rearranged, detailed view should still be the same and contents of neighboring rows should not be affected.

Current Behavior

When rows are expanded and dragged, the detailed view disappeared. Also, contents of the rows between the original row position and the new row position disappeared / are missing. (can be shown in imgur: https://imgur.com/a/lUTxqKV)

Possible Solution

Code Sample

//custom formatter
const statusFormatter: Formatter = (row, cell, value, columnDef, dataContext) => {
  return value ? '<i class="fa fa-circle" style="color: green;" ></i>' : '<i class="fa fa-circle" style="color: red;"></i>';
}

//local storage key for grid state
const LOCAL_STORAGE_KEY = "gridState";

@Component({
  selector: 'app-grid',
  templateUrl: './grid.component.html',
  styleUrls: ['./grid.component.css']
})
export class GridComponent implements OnInit, OnDestroy {

  constructor(private translate: TranslateService,
              private dataSvc: DataService){
    
  }

  columnDefinitions: Column[] = [];
  gridOptions: GridOption = {};
  dataset: any[] = [];
  angularGrid: AngularGridInstance; 

  originalState: any;
  num: number = 0;

  gridSubscription: Subscription;

  ngOnInit(): void {
    const presets = JSON.parse(localStorage[LOCAL_STORAGE_KEY] || null);
    this.defineGrid(presets);
  }

  ngOnDestroy(){
    if(this.gridSubscription)
      this.gridSubscription.unsubscribe();
  }

  resetPreset(e: any){
    localStorage.setItem(LOCAL_STORAGE_KEY, null);
    // this.defineGrid(null);   => weird behavior
    this.angularGrid.gridService.resetGrid(this.columnDefinitions); //correct 
  }

  defineGrid(gridStatePresets?: GridState){
    this.columnDefinitions = [
      { id: '#', field: '', name:'', width: 40, behavior: 'selectAndMove', selectable: false, resizable: true, cssClass: 'cell-reorder dnd'},
      { id: 'status', name: 'Status', field: 'status', formatter: statusFormatter},
      { id: 'date', name: 'Date', field: 'date', sortable: true}
    ];
    this.gridOptions = {
      enableAutoResize: true,       // true by default
      enableCellNavigation: true,
      enableRowMoveManager: true,
      rowMoveManager: {
        onBeforeMoveRows: (e, args) => this.onBeforeMoveRows(e, args),
        onMoveRows: (e, args) => this.onMoveRows(e, args),
        cancelEditOnDrag: true
      },
      enableRowDetailView: true,
      rowDetailView: {
        process: (item) => this.setSelectedAirline(item),
        loadOnce: false,
        singleRowExpand: false,
        useRowClick: true,
        preloadComponent: SpinnerComponent,
        viewComponent: RowDetailViewComponent,
        panelRows: 3
        // expandableOverride: () => {return false}
      }
    };

    this.gridSubscription = this.dataSvc.apiData.subscribe(
      data => {
        this.dataset = data;
      }
    );

    if(gridStatePresets) 
      this.gridOptions.presets = gridStatePresets;
  }


  angularGridReady(angularGrid: AngularGridInstance){
    this.angularGrid = angularGrid;
  }

  saveGridState(grid) {
    //parses object into string to store in local storage
    localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(this.angularGrid.gridStateService.getCurrentGridState()));
  }

  onBeforeMoveRows(e, data){
    for (let i = 0; i < data.rows.length; i++) {
      // no point in moving before or after itself
      if (data.rows[i] === data.insertBefore || data.rows[i] === data.insertBefore - 1) {
        e.stopPropagation();
        return false;
      }
    }
    return true;
  }
  
  setSelectedAirline(item: airlineStatus){
    this.dataSvc.selectedData.next(item);
    return this.dataSvc.selectedData;
  }

  onMoveRows(e, args) {
    const extractedRows = [];
    let left;
    let right;
    const rows = args.rows;
    const insertBefore = args.insertBefore;
    left = this.dataset.slice(0, insertBefore);
    right = this.dataset.slice(insertBefore, this.dataset.length);
    rows.sort((a, b) => {
      return a - b;
    });

    for (let i = 0; i < rows.length; i++) {
      extractedRows.push(this.dataset[rows[i]]);
    }

    rows.reverse();

    for (let i = 0; i < rows.length; i++) {
      const row = rows[i];
      if (row < insertBefore) {
        left.splice(row, 1);
      } else {
        right.splice(row - insertBefore, 1);
      }
    }
    this.dataset = left.concat(extractedRows.concat(right));
    const selectedRows = [];

    for (let i = 0; i < rows.length; i++) {
      selectedRows.push(left.length + i);
    }

    this.angularGrid.slickGrid.resetActiveCell();
    this.angularGrid.slickGrid.setData(this.dataset);
    this.angularGrid.slickGrid.setSelectedRows(selectedRows);
    this.angularGrid.slickGrid.render();
  }
}

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:9 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
ghiscodingcommented, Aug 1, 2019

oh wow it’s incredible to see what users can do with 2 features that were never tested together lol. What I mean is that these 2 features were designed and created independently in the SlickGrid core library, and we never tested dragging and row detail (the later is fairly new, just a few months old) and there’s probably still some bugs like yours that may arise when combined with other plugins (row detail & draggable row are 2 separate plugins within the core lib).

Anyhow I think that we might need to subscribe internally to the draggable event and close all row details when that happens. There’s way too much logic to put in when dealing with row detail and moving rows. For example, we do exactly that when doing a sort, you’ll see that when you sort it closes all row details.

You might be able to apply a quick fix to collapse all row detail before moving the rows. Try the following in your code

onBeforeMoveRows(e, data) {
   // collapse all row detail panels before moving any rows
   const rowDetailInstance = this.angularGrid.extensionService.getSlickgridAddonInstance(ExtensionName.rowDetailView);
   collapseAll();

   // the rest of your code
   // ...
}

Since these 2 plugins are rarely used together, I think that would be the only option and I don’t think I should add that in the lib itself because the onBeforeMoveRows event is only available when using the rowMoveManager option. In other words, it has to be fixed in your code (with what I provided, hopefully that works), please confirm that it works (I didn’t have time to create a Wiki for the Row Detail, but that info should go in a Wiki).

1reaction
ghiscodingcommented, Aug 1, 2019

The method getSlickgridAddonInstance gives you the instance of the SlickGrid plugin, these plugins in SlickGrid are opt in, they are not part of the core library itself. Basically if you want this feature (for example Row Detail), you need to add the SlickGrid plugin to your code. To make it easier, what I did in Angular-Slickgrid is that the user simply need to enable a flag (enableRowDetail in your case) and the lib will take care of importing and loading the correct SlickGrid plugin. You can see a list of all the SlickGrid Plugins, again like I said they are opt in but I made them all available in Angular-Slickgrid with simple flags to turn on/off.

Now that you understand what SlickGrid plugins are, there are also Controls, which are very similar to Plugins and are opt in as well. You can see the SlickGrid Controls (basically, ColumnPicker, GridMenu are the main ones).

So with all of that, I decided to use the term Extensions in my lib to cover the entire set of Controls/Plugins (here’s the list of Extensions in Angular-Slickgrid), each of these Controls/Plugins are actual Extension in my code and are totally separate, which I found much better for debugging purposes (my first implementation was all 1 big Service and that was messy, now they are all separate).

Then finally, these Controls/Plugins are called in the core lib by newing them (for example new Slick.Plugins.RowDetailView({ options }), the getSlickgridAddonInstance will return you the instance of that new plugin call. Since we need to close all row detail, we need to call the plugin’s method, which is why you need to call the getSlickgridAddonInstance and from there you can then call the collapseAll.

I should probably put that in a Wiki somewhere 🤣 Does that make any sense? Feel free to ask, as I do would like to copy + paste this in a Wiki

Cheers ⭐️

Read more comments on GitHub >

github_iconTop Results From Across the Web

Drag and drop rows within QTableWidget - Stack Overflow
This seems very bizarre default behaviour. ... The currently selected row doesn't follow the drag and drop (so if you move the third...
Read more >
Drag Table rows up and down - Google Cloud Community
Feature to drag rows up and down in table view will be great way to schedule tasks and prioritize things for a manufacturing...
Read more >
overscroll-behavior - CSS: Cascading Style Sheets | MDN
The overscroll-behavior CSS property sets what a browser does when reaching the boundary of a scrolling area.
Read more >
GridView for MVC - A Working Example of Drag & Drop Row ...
To assign a row number, create an unbound column and handle the ASPxGridView.CustomUnboundColumnData event. See the sample and video.
Read more >
DataGrid with Movable Rows - CodeProject
As the grid is scrolled vertically, the top rows that are going out of view are actually getting hidden, rather than simply being...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found