# MultiRow 101

This page shows how to get started with Wijmo's MultiRow control.

## Getting Started

The MultiRow control extends conventional grid layouts by using multiple rows to represent each data item.

The MultiRow control allows users to see and edit data in a tabular form, just like other conventional grids. But, MultiRow is different from these grids in a way that it allows you to bind each data item to multiple rows, creating form-like interfaces that can display a large number of columns with minimal horizontal scrolling.

The MultiRow control extends the FlexGrid control, so if you know how to use FlexGrid, you will be able to use MultiRow in no time. The main new property is layoutDefinition, which takes an object that describes the layout of the grid rows and cells.

The MultiRow control is not a simple replacement for conventional grids; it is a specialized tool that fits some particular scenarios really well.

To use the MultiRow control in your applications, add references to the wijmo, wijmo.grid, and wijmo.grid.multirow modules, then instantiate MultiRow controls by giving them host element on the page, or add wj-multi-row directives if you are using AngularJS:

<script src="node_modules/es6-shim/es6-shim.min.js"></script> <script src="node_modules/zone.js/dist/zone.js"></script> <script src="node_modules/reflect-metadata/Reflect.js"></script> <script src="node_modules/systemjs/dist/system.src.js"></script> <!-- Configure SystemJS --> <script src="systemjs.config.js"></script> <!-- JQuery/Bootstrap --> <link rel="stylesheet" type="text/css" href="bin/Devel/external/bootstrap/css/bootstrap.css" /> <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" /> <!-- Wijmo --> <script wj-src="wijmo" src="bin/Devel/loaders/wijmo.load.module.js" type="text/javascript"></script> <script wj-src="wijmo.grid" src="bin/Devel/loaders/wijmo.load.module.js"></script> <script wj-src="wijmo.grid.multirow" src="bin/Devel/loaders/wijmo.load.module.js"></script> <script wj-src="wijmo.input" src="bin/Devel/loaders/wijmo.load.module.js"></script> <script wj-src="wijmo.grid.filter" src="bin/Devel/loaders/wijmo.load.module.js"></script> <link href="bin/Devel/sources/styles/wijmo.css" rel="stylesheet" /> <!-- Wijmo Angular2 --> <script wj-src="wijmo.angular2" src="bin/Devel/loaders/wijmo.load.module.js" type="text/javascript"></script> <!-- Load the root application module --> <script> System.import('./src/app'); </script> <!-- using AngularJS --> <wj-multi-row [itemsSource]="data" [layoutDefinition]="layoutDef"> </wj-multi-row> <!-- using pure JS --> <div id="multirow"></div> </body> </html>
// Angular import * as wjcCore from 'wijmo/wijmo'; import * as wjcGrid from 'wijmo/wijmo.grid'; import * as wjcGridFilter from 'wijmo/wijmo.grid.filter'; import { Component, EventEmitter, Input, Inject, enableProdMode, ViewChild, AfterViewInit, NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { CommonModule } from '@angular/common'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { WjGridMultirowModule } from 'wijmo/wijmo.angular2.grid.multirow'; import { DataSvc } from './services/DataSvc'; 'use strict'; // The Explorer application root component. @Component({ selector: 'app-cmp', templateUrl: 'src/app.html' }) export class AppCmp{ orders: any[]; constructor( @Inject(DataSvc) dataSvc: DataSvc) { this.orders = dataSvc.orders; this.layoutDefs = dataSvc.layoutDefs; } } @NgModule({ imports: [WjGridMultirowModule, BrowserModule, FormsModule, TabsModule], declarations: [AppCmp], providers: [DataSvc], bootstrap: [AppCmp] }) export class AppModule { } enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule);

## MultiRow Layout Definition

The MultiRow control uses multiple rows to display each record. The record layout is defined by the layoutDefinition property.

The layoutDefinition property contains an array of cell group objects with the following properties:

- header: Group name.
- colspan: Number of columns spanned by the group (defaults to one).
- cells: Array of objects representing the cells in the group. Cells are columns with an additional colspan property.

The example below illustrates the usage of the layoutDefiniton property:

Now, let's look at the same data shown in a MultiRow control with different layouts. Use the combo box to select a layout definition and how the grid groups the data in different ways. Notice that the MultiRow control provides all the usual grid features, including editing, sorting, filtering, and column sizing:

<wj-multi-row [itemsSource]="orders" [layoutDefinition]="layoutDefs.currentItem.def"> </wj-multi-row> <wj-combo-box [itemsSource]="layoutDefs" [displayMemberPath]="'name'"> </wj-combo-box> <p> {​{layoutDefs.currentItem.description}​} </p>
// sample layout definitions this.layoutDefs = new wjcCore.CollectionView([ { name: 'Traditional', description: 'Traditional grid view, with one row per record. The user must scroll horizontally to see the whole record.', def: [ { cells: [{ binding: 'id', header:'ID' }]}, { cells: [{ binding: 'date', header:'Ordered' }]}, { cells: [{ binding: 'shippedDate', header:'Shipped' }]}, // ... one group per column ... ] }, { name: 'Compact', description: 'This view uses two rows per record. The layout is divided into three column groups: order, customer, and shipper', def: [ { header: 'Order', colspan: 2, cells: [ { binding: 'id', header: 'ID' }, { binding: 'date', header: 'Ordered' }, { binding: 'amount', header: 'Amount' }, { binding: 'shippedDate', header: 'Shipped' } ] }, // ... two more groups for customers and shippers ... ] }, { name: 'Detailed', description: 'This view uses three rows per record. The layout is divided into three column groups: order, customer, and shipper', def: [ { header: 'Order', colspan: 2, cells: [ { binding: 'id', header: 'ID', colspan: 2 }, { binding: 'amount', header: 'Amount', colspan: 2 }, { binding: 'date', header: 'Ordered' }, { binding: 'shippedDate', header: 'Shipped' } ] }, // ... two more groups for customers and shippers ... ] } ]);

#### Result (live):

{{layoutDefs.currentItem.description}}

The layoutDefinition property specifies the layout of the cells in the grid. It contains an array of cell group objects. Each cell group specifies how many columns the group should span, and the cells that make up each group.

The image below illustrates how a cell group is interpreted and turned into a grid layout:

The group spans three grid columns. It contains six cells with different spans. When generating the layout, the grid fits as many cells as possible in each row, and wraps to the next row when the group span is reached. The last cell in each row is automatically expanded to fill colspan of the group. The process is similar to wrapping of text to create a paragraph.

The same process is applied to every group in the layoutDefinition object.

## Collapsible Column Headers

By default, the MultiRow control creates column headers that span multiple rows and shows the header for each cell defined in the layoutDefinition.

These cell-specific column headers may be used to sort or filter the data as you would do in a conventional grid.

In some cases, you may want to collapse the column headers to a single line, showing only the group names rather than individual cells. This saves space at the expense of having individual cell headers. To collapse the column headers, set the collapsedHeaders property to true. In these scenarios, remember to set the header property on the groups in order to avoid empty column headers.

<wj-multi-row [itemsSource]="orders" [layoutDefinition]="ldThreeLines" [collapsedHeaders]="true" [showHeaderCollapseButton]="true" #hdrGrid> </wj-multi-row> <label> <input type="checkbox" [(ngModel)]="hdrGrid.collapsedHeaders"> Collapsed Headers </label> <br /> <label> <input type="checkbox" [(ngModel)]="hdrGrid.showHeaderCollapseButton"> Show Header Collapse Button </label>
// no code required

#### Result (live):

Collapsed Headers
Show Header Collapse Button

## Fully Expanded Column Headers

Setting the collapsedHeaders property to true causes the grid to show a single row of column headers containing the group names. Setting it to false cause it to show a group of rows with the same layout as the data, and column names in the cells.

If you want to display the group and the column names, set the collapsedHeaders property to null instead of true or false.

If you choose to show group and column headers, you may also want to use CSS to differentiate their appearance. This can be done easily using the `wj-group-header` class added to the group headers.

The sample below demonstrates:

<wj-multi-row [itemsSource]="orders" [layoutDefinition]="ldThreeLines" class="expanded-groups" #expandedHeadersGrid> </wj-multi-row>
// setting collapsedHeaders to null shows // both group and column headers ngAfterViewInit() { this.expandedHeadersGrid.collapsedHeaders = null; }
/* custom styling for group headers */ .expanded-groups .wj-cell.wj-group-header { background-color: #abcfc7; }

#### Result (live):

## Styling Records, Groups, and Cells

In most of the applications, you would want to show where each record and group starts or ends. The MultiRow control enables this by adding CSS class names to cell elements in the first and last row/column of each group. The class names are `wj-record-start`, `wj-record-end`, `wj-group-start`, and `wj-group-end`.

The example below shows how you can use these class names in CSS rules to customize the appearance of the record and group delimiters. It also shows how you can use the standard cssClass property to customize the appearance of specific cells within a group:

<wj-multi-row class="multirow-css" [itemsSource]="orders" [layoutDefinition]="ldThreeLines"> </wj-multi-row>
/* custom styling for a MultiRow */ .multirow-css .wj-cell.wj-record-end:not(.wj-header) { border-bottom-color: #8fabff; /* blue lines between records */ } .multirow-css .wj-cell.wj-group-end { border-right-color: #bc5505; /* brown lines between groups */ } .multirow-css .wj-cell.id { color: #c0c0c0; } .multirow-css .wj-cell.amount { color: #014701; font-weight: bold; } .multirow-css .wj-cell.email { color: #0010c0; text-decoration: underline; }

#### Result (live):

## Grouping

The MultiRow control supports CollectionView-based grouping just like FlexGrid. To use grouping, create a CollectionView based on the raw data and add one or more GroupDescription objects to the GroupDescriptions array of the collection.

<wj-multi-row [itemsSource]="groupedOrders" [layoutDefinition]="ldThreeLines" [groupHeaderFormat]="'City: <b>{value} </b>({count:n0} items)'" #groupingGrid> </wj-multi-row> <label> <input type="checkbox" [(ngModel)]="groupingGrid.showGroups"> Show Groups </label> <br /> <button class="btn" (click)="groupingGrid.collapseGroupsToLevel(0)"> Collapse All </button> <button class="btn" (click)="groupingGrid.collapseGroupsToLevel(10)"> Expand All </button>
// expose grouped orders to the controller this.groupedOrders = new wjcCore.CollectionView(orders, { groupDescriptions: [ 'customer.city', ], newItemCreator: function () { return { // add empty customer and shipper objects to new orders customer: {}, shipper: {} } }, });

#### Result (live):

Show Groups

## Filtering

The MultiRow control supports filtering just like FlexGrid.

Filtering is provided by the wijmo.grid.filter.FlexGridFilter class. To add filtering UI to MultiRow, create a FlexGridFilter and pass MultiRow as a parameter in the constructor.

If you are using AngularJS, you can also add a filter to the grid by embedding a wj-flex-grid-filter directive as a child of the grid's directive.

<wj-multi-row [itemsSource]="orders" [layoutDefinition]="ldThreeLines" #filterGrid> </wj-multi-row>
// add a filter to the MultiRow ngAfterViewInit() { var filter = new wjcGridFilter.FlexGridFilter(this.filterGrid); }

#### Result (live):

## Cell Freezing

The MultiRow control allows you to freeze rows and columns so they remain in view as the user scrolls the grid. Frozen cells can be edited and selected as regular cells, exactly as in Excel and in the FlexGrid control.

This example allows you to toggle whether the first group of rows and columns should be frozen.

<wj-multi-row [itemsSource]="orders" [layoutDefinition]="ldTwoLines" #frozenGrid> </wj-multi-row> <button class="btn btn-default" (click)="toggleFreeze(2, 2)"> {​{ frozenGrid.frozenRows == 0 ? 'Freeze' : 'Unfreeze' }​} </button>
// toggle frozen rows/columns toggleFreeze(rows: number, cols: number) { var flex = this.frozenGrid; if (flex) { flex.frozenColumns = flex.frozenColumns ? 0 : cols; flex.frozenRows = flex.frozenRows ? 0 : rows; } }
/* custom styling for frozen cells */ .wj-cell.wj-frozen:not(.wj-header):not(.wj-group):not(.wj-state-selected):not(.wj-state-multi-selected) { background-color: #f3fbcc; }

#### Result (live):

## Paging

The MultiRow control supports paging through the IPagedCollectionView interface, which is nearly identical to the one in .NET. To enable paging, set the IPagedCollectionView.pageSize property to the number of items you want to display on each page, and provide a UI for navigating the pages.

In this example, we use JavaScript to show four items per page. We add navigation buttons, and call IPagedCollectionView method in the button click directives. We use the pageIndex and pageCount properties to show the current page and total number of pages.

<wj-multi-row style="max-height:800px" [itemsSource]="pagedOrders" [layoutDefinition]="ldThreeLines"> </wj-multi-row> <div class="btn-group"> <button type="button" class="btn" (click)="pagedOrders.moveToFirstPage()"> <span class="glyphicon glyphicon-fast-backward"></span> </button> <button type="button" class="btn" (click)="pagedOrders.moveToPreviousPage()"> <span class="glyphicon glyphicon-step-backward"></span> </button> <button type="button" class="btn" disabled style="width:100px"> {​{pagedOrders.pageIndex + 1 | number}​} / {​{pagedOrders.pageCount | number}​} </button> <button type="button" class="btn" (click)="pagedOrders.moveToNextPage()"> <span class="glyphicon glyphicon-step-forward"></span> </button> <button type="button" class="btn" (click)="pagedOrders.moveToLastPage()"> <span class="glyphicon glyphicon-fast-forward"></span> </button> </div>
// expose paged orders to the controller this.pagedOrders = new wjcCore.CollectionView(orders, { pageSize: 4 });

#### Result (live):

## Adding and Deleting Records

The MultiRow control supports the allowAddNew and allowDelete properties provided by the FlexGrid control.

Setting the allowAddNew property to true causes the grid to display a set of 'new row template' rows at the bottom of the grid. When user starts editing a cell in the new row template, a new item is added to the source collection. When user finishes editing the new item by moving the selection to another row or the focus to another control, the new item is committed.

Setting the allowDelete property to true causes the grid to monitor key presses and to delete the current row if user presses the 'Delete' key while an entire row is selected.

<wj-multi-row [itemsSource]="addNewOrders" [layoutDefinition]="ldThreeLines" [allowAddNew]="true" [allowDelete]="true" #addNewGrid> </wj-multi-row> <label> <input type="checkbox" [(ngModel)]="addNewGrid.allowAddNew"> Allow Add New </label> <br /> <label> <input type="checkbox" [(ngModel)]="addNewGrid.allowDelete"> Allow Delete </label>
// create 'addNewOrders' collection, start with last item selected this.addNewOrders = new wjcCore.CollectionView(orders); this.addNewOrders.moveCurrentToLast();
/* custom styling for new row templates */ .wj-cell.wj-new:not(.wj-header):not(.wj-group):not(.wj-state-selected):not(.wj-state-multi-selected) { background-color: #f3fbcc; }

#### Result (live):

Allow Add New
Allow Delete