Skip to content
Ghislain B edited this page Aug 23, 2019 · 14 revisions

Demo

Demo Page / Demo Component

Description

A Row Detail allows you to open a detail panel which can contain extra and/or more detailed information about about a row. For example, we have a user list but we want to display detailed information about this user (his full address, account info, last purchasers, ...) but we don't want to display this in the user grid (for performance and real estate reasons), so a Row Detail is perfect for this.

Usage

View
<angular-slickgrid 
    gridId="grid2" 
    [columnDefinitions]="columnDefinitions"
    [gridOptions]="gridOptions" 
    [dataset]="dataset"
    (onAngularGridCreated)="angularGridReady($event)">
</angular-slickgrid>
Component
@Component({
  templateUrl: './grid-grouping.component.html'
})
export class GridGroupingComponent implements OnInit, OnDestroy {
  columnDefinitions: Column[];
  gridOptions: GridOption;
  dataviewObj: any;

  angularGridReady(angularGrid: AngularGridInstance) {
    this.angularGrid = angularGrid;
    this.gridObj = angularGrid.slickGrid;
    this.dataViewObj = angularGrid.dataView;
  }

  /* Define grid Options and Columns */
  defineGrid() {
    // prepare a multiple-select array to filter with
    const multiSelectFilterArray = [];
    for (let i = 0; i < NB_ITEMS; i++) {
      multiSelectFilterArray.push({ value: i, label: i });
    }

    this.columnDefinitions = [
      { id: 'title', name: 'Title', field: 'title', sortable: true, type: FieldType.string, width: 70, filterable: true },
      { id: 'duration', name: 'Duration (days)', field: 'duration', formatter: Formatters.decimal, params: { minDecimalPlaces: 1, maxDecimalPlaces: 2 }, sortable: true, type: FieldType.number, minWidth: 90, filterable: true },
      { id: 'percent2', name: '% Complete', field: 'percentComplete2', formatter: Formatters.progressBar, type: FieldType.number, sortable: true, minWidth: 100, filterable: true, filter: { model: Filters.slider, operator: '>' } },
      { id: 'start', name: 'Start', field: 'start', formatter: Formatters.dateIso, sortable: true, type: FieldType.date, minWidth: 90, exportWithFormatter: true, filterable: true, filter: { model: Filters.compoundDate } },
      { id: 'finish', name: 'Finish', field: 'finish', formatter: Formatters.dateIso, sortable: true, type: FieldType.date, minWidth: 90, exportWithFormatter: true, filterable: true, filter: { model: Filters.compoundDate } },
      {
        id: 'effort-driven', name: 'Effort Driven', field: 'effortDriven',
        minWidth: 100,
        formatter: Formatters.checkmark, type: FieldType.boolean,
        filterable: true, sortable: true,
        filter: {
          collection: [{ value: '', label: '' }, { value: true, label: 'True' }, { value: false, label: 'False' }],
          model: Filters.singleSelect
        }
      }
    ];

    this.gridOptions = {
      enableRowDetailView: true,
      rowSelectionOptions: {
        selectActiveRow: true
      },
      rowDetailView: {
        // We can load the "process" asynchronously in 2 different ways (httpClient OR even Promise)
        process: (item) => this.http.get(`api/item/${item.id}`),

        // load only once and reuse the same item detail without calling process method
        loadOnce: true,

        // limit expanded row to only 1 at a time
        singleRowExpand: false,

        // false by default, clicking anywhere on the row will open the detail view
        // when set to false, only the "+" icon would open the row detail
        // if you use editor or cell navigation you would want this flag set to false (default)
        useRowClick: true,

        // how many grid rows do we want to use for the row detail panel (this is only set once and will be used for all row detail)
        // also note that the detail view adds an extra 1 row for padding purposes
        // so if you choose 4 panelRows, the display will in fact use 5 rows
        panelRows: this.detailViewRowCount,

        // you can override the logic for showing (or not) the expand icon
        // for example, display the expand icon only on every 2nd row
        // expandableOverride: (row: number, dataContext: any, grid: any) => (dataContext.id % 2 === 1),

        // Preload View Template
        preloadComponent: RowDetailPreloadComponent,

        // ViewModel Template to load when row detail data is ready
        viewComponent: RowDetailViewComponent,
      }
    };
  }
}

Changing Addon Options Dynamically

Row Detail is an addon (commonly known as a plugin and are opt-in addon), because this is not built-in SlickGrid and instead are opt-in, we need to get the instance of that addon object. Once we have the instance, we can use getOptions and setOptions to get/set any of the addon options, for the list of options available you can see them directly in the addon row detail on the 6pac/SlickGrid repo.

Examples

  • Dynamically change the Detail View Row Count (how many grid rows do we want to use for the row detail panel)
changeDetailViewRowCount() {
  if (this.angularGrid && this.angularGrid.extensionService) {
    const rowDetailInstance = this.angularGrid.extensionService.getSlickgridAddonInstance(ExtensionName.rowDetailView);
    const options = rowDetailInstance.getOptions();
    options.panelRows = this.detailViewRowCount; // change number of rows dynamically
    rowDetailInstance.setOptions(options);
  }
}

Changing Addon Methods Dynamically

Same as previous paragraph, after we get the SlickGrid addon instance, we can call any of the addon methods, for the complete list of methods available you can refer to the addon Row Detail on the 6pac/SlickGrid repo.

Examples

  • Dynamically close all Row Detail Panels
closeAllRowDetail() {
  if (this.angularGrid && this.angularGrid.extensionService) {
    const rowDetailInstance = this.angularGrid.extensionService.getSlickgridAddonInstance(ExtensionName.rowDetailView);
    rowDetailInstance.collapseAll();
  }
}
  • Dynamically close a single Row Detail by it's grid index This requires a bit more work, you can call the method collapseDetailView(item) but it requires to pass the row item object (data context) and it feasible but it's just more work as can be seen below.
closeRowDetail(gridRowIndex: number) {
  if (this.angularGrid && this.angularGrid.extensionService) {
    const rowDetailInstance = this.angularGrid.extensionService.getSlickgridAddonInstance(ExtensionName.rowDetailView);
    const item = this.angularGrid.gridService.getDataItemByRowIndex(gridRowIndex);
    rowDetailInstance.collapseDetailView(item);
  }
}

Row Detail - Preload Component (loading spinner)

Most of the time we would get data asynchronously, during that time we can show a loading spinner to the user via the preloadComponent grid option.

View
import { Component } from '@angular/core';

@Component({
  template: `<h4><i class="fa fa-refresh fa-spin fa-2x fa-fw"></i>Loading...</h4>`
})
export class RowDetailPreloadComponent {}
Component
    this.gridOptions = {
      enableRowDetailView: true,
      rowDetailView: {
        //  ... row detail options

        // Preload View Component
        preloadComponent: RowDetailPreloadComponent,
      }
    };

Row Detail - View Component (loading spinner)

Same concept as the preload, we pass an Angular Component to the viewComponent that will be used to render our Row Detail.

Grid Component
    this.gridOptions = {
      enableRowDetailView: true,
      rowDetailView: {
        //  ... row detail options

        // View Component to load when row detail data is ready
        viewComponent: RowDetailViewComponent,
      }
    };
Row Detail View (rowdetail-view.component.html)
<div class="container-fluid">
    <h2>{{model?.title}}</h2>
    <div class="row">
      <div class="col-xs-3"><label>Assignee:</label> <input class="form-control" [(ngModel)]="model.assignee" /></div>
      <div class="col-xs-3"><label>Reporter:</label> <span>{{model?.reporter}}</span></div>
      <div class="col-xs-2"><label>Duration:</label> <span>{{model?.duration}}</span></div>
      <div class="col-xs-2"><label>% Complete:</label> <span>{{model?.percentComplete}}</span></div>
    </div>

    <div class="row">
      <div class="col-xs-3"><label>Start:</label> <span>{{model?.start | date: 'yyyy-MM-dd'}}</span></div>
      <div class="col-xs-3"><label>Finish:</label> <span>{{model?.finish | date: 'yyyy-MM-dd'}}</span></div>
      <div class="col-xs-2"><label>Effort Driven:</label> <i [class]="model?.effortDriven ? 'fa fa-check' : ''"></i></div>
    </div>

    <hr>

    <h4>
      Find out who is the Assignee
      <small>
        <button class="btn btn-primary btn-sm" (click)="alertAssignee(model?.assignee)">Click Me</button>
      </small>
    </h4>
  </div>
Row Detail Component rowdetail-view.component.ts)
import { Component } from '@angular/core';

@Component({
  templateUrl: './rowdetail-view.component.html'
})
export class RowDetailViewComponent {
  model: {
    duration: Date;
    percentComplete: number;
    reporter: string;
    start: Date;
    finish: Date;
    effortDriven: boolean;
    assignee: string; title: string;
  };

  constructor() {}

  alertAssignee(name: string) {
    if (typeof name === 'string') {
      alert(`Assignee on this task is: ${name.toUpperCase()}`);
    } else {
      alert('No one is assigned to this task.');
    }
  }
}

Contents

Clone this wiki locally