"No Row To Show" message is coming after data binding from Services - ag-grid

The "No Row To Show" message is showing in grid, but the grid one row of data.
I have a demoComponent.ts and html file.
please refer my below code
demoComponent.ts
import { GridOptions } from "ag-grid/main";
import { Service } from '../Services/service';
#Component({
.
templateUrl: 'demoComponent.html',
.
})
#Injectable()
export class demoComponent implements OnInit {
GridOptions: GridOptions;
GridColumnDefs: any[];
GridData: any[];
data1 : any[]
constructor(private Service: Servicee) {
}
ngOnInit() {
this.GridOptions = <GridOptions>{
enableFilter: false,
rowSelection: 'multiple',
enableSorting: true,
enableColResize: true,
singleClickEdit: true,
animateRows: true,
headerHeight: 50,
rowClass: 'rowClass'
};
Observable.forkJoin(
this.Service.methed1(this.id),
this.Service.methed1(this.id),
this.Service.methed1(this.id),
this.Service.methed1()
).subscribe(response => {
this.data1 = <any>response[0];
this.data2 = <any>response[1];
this.data3 = <any>response[2];
this.data4 = <any>response[3];
this.GridBind()
});
}
GridBind()
{
this.GridColumnDefs = [
{ headerName: "", field: "id", hide: true },
{ headerName: "Column1", field: "Column1", width: 70, editable: true, tooltipField: "release", headerTooltip: "Release" },
{ headerName: "Column2", field: "Column2", width: 80, editable: true, tooltipField: "Column2", headerTooltip: "Column2",
//cellEditorFramework: EstimatingSourceComponent
cellEditorFramework: EstimatingSourceComponent,
cellEditorParams: {
dropDownValue: this.data1,
}
},
{ headerName: "Column3", field: "Column3", width: 70, editable: true },
{ headerName: "Column4", field: "Column4", width: 70, editable: true },
];
this.GridData = [
{
id: 1, Column1: this.data1.value1, Column2: this.data1.value2, Column3: this.data1.value3, Column4: this.data1.value4
}
];
}
}
Then the demoComponent.html
<ag-grid-angular style="width: 100%; height: 350px;"
id="grid1"
class="ag-fresh"
[rowData]="GridData"
[columnDefs]="GridColumnDefs"
[gridOptions]="GridOptions">
</ag-grid-angular>
on rendering the one row is binded with the grid. but "No Row To Show" is showing.
Please correct my implementation , if it is wrong.
thanks in advance

Related

Ag-grid is taking a lot of time to display with multiple Cell Renderer and Value Getter

I have to render ag-grid table in dialog box.
My grid has multi cellRenderer, valueGetter and cellRendererParams in columnDefs.
Instead of converting server data to rowData and using field to render, I have used valueGetter to fetch the value from the server data like
valueGetter: 'data.screen1.graph.visible'.
My ag-grid with 13 columns and 150 rows is taking noticeable time to render.
Is it because of valueGetter that my table is taking time to render?
Below is my sample data:
private createColumnDefs() {
this.columnDefs = [{
headerName: 'Header1',
cellRenderer: (params) => {
return params.data.serverObject1.function === 'input' ? 'I' : 'O';
},
minWidth: 50,
width: 70,
}, {
headerName: 'Header2',
field: 'serverObject1.name',
minWidth: 60,
width: 80,
}, {
headerName: 'Header3',
field: 'value',
cellRenderer: 'valueFieldRenderer',
minWidth: 100,
width: 250,
}, {
headerName: 'Header4',
cellRenderer: 'checkBoxRenderer',
valueGetter: 'data.serverObject1.graph.visible',
cellRendererParams(params) {
return {
disabledVal : params.data.localArray.length > 0 || params.data.attribute1 !== null
};
},
minWidth: 70,
width: 70,
}, {
headerName: 'Header5',
cellRenderer: 'checkBoxRenderer',
valueGetter: 'data.serverObject1.archive',
cellRendererParams(params) {
return {
disabledVal : params.data.serverObject1.function === 'input'
};
},
minWidth: 70,
width: 70,
}, {
headerName: 'Header6',
cellRenderer: 'checkBoxRenderer',
valueGetter: 'data.serverObject1.alarm',
cellRendererParams(params) {
return {
disabledVal : !((params.data.serverObject1.function === 'output') && (params.data.serverObject1.type === 'bool'))
};
},
minWidth: 70,
width: 70,
},
{
headerName: 'Header7',
cellRenderer: 'textBoxRenderer',
valueGetter: 'data.serverObject1.active',
minWidth: 150,
width: 150,
},
{
headerName: 'Header8',
cellRenderer: 'textBoxRenderer',
valueGetter: 'data.serverObject1.inactive',
minWidth: 150,
width: 150,
},
{
headerName: 'Header11',
cellRenderer: 'textBoxRenderer',
valueGetter: (params) => params.data.text1,
minWidth: 150,
width: 150,
cellStyle: (params) => {
return (this.diagram === 'XYZ' &&
!(params.data.serverObject1.function === 'output' || params.data.serverObject1.type === 'string'
|| params.data.serverObject1.type === 'int')) ? {display: 'block'} : {display: 'none'};
}}
];
}
cell renderer:
#Component({
selector: 'app-check-box',
template:
`
<input type="checkbox" [(ngModel)]="params.value" [id]="getID(params)" class="primary small"
(change)="onChange($event)">
<label [for]="getID(params)" class="text-hide">hidden</label>
`,
styleUrls: ['./test.component.css']
})
// tslint:disable-next-line:component-class-suffix
export class CheckBoxRenderer implements AfterViewInit, ICellRendererAngularComp {
private params: ICellRendererParams;
constructor(private readonly translateService: TranslateService) { }
agInit(params: ICellRendererParams): void {
this.params = params;
}
public onChange(event) {
}
refresh(params: any): boolean {
return false;
}
public getID(params): string {
return params.column.colId + params.rowIndex;
}
afterGuiAttached(params?: IAfterGuiAttachedParams): void {
}
ngAfterViewInit(): void {
}
translate(key: string, params?: any): string {
return this.translateService.instant(key, params);
}
}
Should I consider converting to rowData to decrease loading time?

ag-grid data for detailCellRendererParams not displaying

I'm trying to add a nested grid inside an existing grid. I'm very new at this, and although I'm able to display the grid, I cannot seem to display the data. When I do console.log(params), I have all my data, but no matter what I try, I cannot display the data. I think the problem is with my getDetailRowData, and what I'm passing to the successCallback function. Any ideas?
this is my component ts file
import { Component, OnInit } from '#angular/core';
import { ReportService } from 'src/app/services/report.service';
import { Report } from 'src/app/models/report';
import { agThemeSgBootstrap } from "#sg-bootstrap/ag-grid";
import {GridOptions, Module} from 'ag-grid-community';
import { DatePipe } from '#angular/common';
#Component({
selector: 'app-report',
templateUrl: './report.component.html',
styleUrls: ['./report.component.scss']
})
export class ReportComponent implements OnInit {
apiUrl: any;
reports : Report[];
gridOptions: GridOptions;
dateValue = new Date();
maxDate = new Date();
private detailCellRendererParams;
private columnDefs;
// public modules: Module[] = AllModules;
constructor(private reportService : ReportService, private datePipe: DatePipe) {
this.columnDefs = [
{headerName: 'Report Name', field: 'reportName', cellRenderer: 'agGroupCellRenderer',
resizable: true,
valueGetter: params => { return `${params.data.reportName}.dat` }},
{headerName: 'Sent to FIS', field: 'sentToFis',resizable: false,
valueGetter: params => { return params.data.sentToFis === true ? "Yes" : "No" } },
{headerName: 'File Size', field: 'contentLength', resizable: true,
valueGetter: params => { return `${this.formatBytes(params.data.contentLength)}` }},
{headerName: 'Last Modified', field: 'lastModified', resizable: true,
valueGetter: params => { return this.datePipe.transform(params.data.lastModified, "yyyy-MM-dd HH:mm:ss") }},
];
this.detailCellRendererParams = {
detailGridOptions: {
columnDefs: [
{headerName: 'ADSL Path', field: 'adslPath', resizable: true,
valueGetter: params => { return params.data.adlsFullPath}},
{headerName: 'Version', field: 'version', resizable: true,
valueGetter: params => { return params.data.sentToFis}},
{headerName: 'Source', field: 'source', resizable: true,
valueGetter: params => { return params.data.adlsFullPath}},
],
},
getDetailRowData: function(params) {
params.successCallback(params.data.children);
console.log(params.data);
}
}
}
ngOnInit() {
this.callReportService(new Date())
}
reportDateChange(value: Date) {
let currentValue = this.datePipe.transform(this.dateValue, "yyyyMMdd")
let newSelectedValue = this.datePipe.transform(value, "yyyyMMdd")
if (currentValue != newSelectedValue) {
if (this.gridOptions.api) {
this.gridOptions.api.showLoadingOverlay();
}
this.callReportService(value)
this.dateValue = value;
}
}
callReportService(value: Date) {
this.reportService.getReportsForDate(value).subscribe(x=> {
this.reports = x;
this.gridOptions.api.sizeColumnsToFit();
})
}
ngAfterContentInit() {
let agOpt = { ...{
animateRows: true,
enableRangeSelection: true,
defaultColDef: {
editable: false,
enableValue: false,
enableRowGroup: false,
enablePivot: true,
filter: true,
sortable: true
},
statusBar: {
statusPanels: [{
statusPanel: 'agTotalRowCountComponent',
align: 'left'
},
{
statusPanel: 'agFilteredRowCountComponent'
},
{
statusPanel: 'agSelectedRowCountComponent'
},
{
statusPanel: 'agAggregationComponent'
},
],
}
}, ...agThemeSgBootstrap }
this.gridOptions = { ...this.gridOptions, ...agOpt }
}
onGridReady(params) {
params.api.setDomLayout("autoHeight");
params.api.sizeColumnsToFit();
}
onGridSizeChanged(params) {
params.api.sizeColumnsToFit();
}
ngAfterViewInit() {
this.dateValue = new Date()
}
formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
}
my html file
<div class="container-fluid">
<div class="col-xs-12 col-12 col-md-3 form-group row">
<label for="reportDate" class="col-form-label">Report Date:</label>
<div class="col-sm-4 col-md-7">
<input id="reportDate"
class="form-control"
#dp="bsDatepicker"
(bsValueChange)="reportDateChange($event)"
[maxDate]="maxDate"
bsDatepicker
[bsValue]="dateValue" [bsConfig]="{ isAnimated: true, adaptivePosition: true, dateInputFormat: 'YYYY-MM-DD', containerClass: 'theme-red' }"/>
</div>
</div>
<div class="row">
<div class="col">
<ag-grid-angular
style="width: 100%; height: 300px;"
class="ag-theme-sg-bootstrap"
[gridOptions]="gridOptions"
[masterDetail]="true"
[detailCellRendererParams]="detailCellRendererParams"
[rowData]="reports"
[columnDefs]="columnDefs"
(gridReady)="onGridReady($event)"
(gridSizeChanged)="onGridSizeChanged($event)">
</ag-grid-angular>
</div>
</div>
</div>

how to use multiple custom components in framework in ag-grid-angular?

I have created two custom components, i.e custom tooltip component and custom datepicker component. when im trying to declare in framework it is not working.
Its taking only datepicker custom component not tooltip component. If datepicker component is removed then its taking tooltip component Its considering only one custom component in frameworkComponent.
Please find the below code:
CustomTooltip :
import {Component, ViewEncapsulation} from '#angular/core';
import {ITooltipAngularComp} from "ag-grid-angular";
#Component({
selector: 'tooltip-component',
template: `
<div class="custom-tooltip" [style.background-color]="data.color">
<p>{{tooltipData}}</p>
</div>`,
styles: [
`
:host {
position: absolute;
width: 250px;
height: 60px;
border: 1px solid cornflowerblue;
overflow: hidden;
pointer-events: none;
transition: opacity 1s;
}
:host.ag-tooltip-hiding {
opacity: 0;
}
.custom-tooltip p {
margin: 5px;
white-space: nowrap;
}
`
],
styleUrls: ['./custom-tooltip.component.scss']
})
export class CustomTooltip implements ITooltipAngularComp {
params: any;
data: any;
tooltipData: any;
agInit(params): void {
console.log("params",params.value);
this.params = params;
this.tooltipData=params.value;
this.data = params.api.getRowNode(params.rowIndex).data;
this.data.color = this.params.color || 'white';
}
}
CustomDateComponent:
import {Component, ElementRef, ViewChild} from '#angular/core';
import flatpickr from 'flatpickr'
#Component({
selector: 'app-loading-overlay',
template: `
<div #flatpickrEl class="ag-input-text-wrapper custom-date-filter fa">
<input type='text' data-input />
<a class='input-button' title='clear' data-clear>
<i class='fa fa-times'></i>
</a>
<a class="input-button" title="toggle" data-toggle>
<i class="fa fa-calendar"></i>
</a>
</div>
`,
styles: [
`
.custom-date-filter a {
position: relative;
right: 34px;
color: rgba(0, 0, 0, 0.54);
cursor: pointer;
}
.custom-date-filter:after {
content: '\f073';
display: block;
font-weight: 400;
font-family: 'Font Awesome 5 Free';
position: relative;
right: 25px;
pointer-events: none;
color: rgba(0, 0, 0, 0.54);
}
`
]
})
export class CustomDateComponent {
#ViewChild("flatpickrEl", {read: ElementRef}) flatpickrEl: ElementRef;
private date: Date;
private params: any;
private picker: any;
agInit(params: any): void {
this.params = params;
}
ngAfterViewInit(): void {
// outputs `I am span`
this.picker = flatpickr(this.flatpickrEl.nativeElement, {
onChange: this.onDateChanged.bind(this),
wrap: true
});
this.picker.calendarContainer.classList.add('ag-custom-component-popup');
}
ngOnDestroy() {
console.log(`Destroying DateComponent`);
}
onDateChanged(selectedDates) {
this.date = selectedDates[0] || null;
this.params.onDateChanged();
}
getDate(): Date {
return this.date;
}
setDate(date: Date): void {
this.date = date || null;
this.picker.setDate(date);
}
}
Im trying to use both custom component in one grid i.e:
this.columnDefs = [
{
headerName: 'Request Number', field: 'request_no', sortable: true, filter: 'agNumberColumnFilter'
},
{
headerName: 'Request Date', field: 'created_at', sortable: true, width: 300,
filter: "agDateColumnFilter",
filterParams: {
comparator: function (filterLocalDateAtMidnight, cellValue) {
var dateAsString = cellValue;
var dateParts = dateAsString.split("/");
var cellDate = new Date(Number(dateParts[2]), Number(dateParts[1]) - 1, Number(dateParts[0]));
if (filterLocalDateAtMidnight.getTime() === cellDate.getTime()) {
return 0;
}
if (cellDate < filterLocalDateAtMidnight) {
return -1;
}
if (cellDate > filterLocalDateAtMidnight) {
return 1;
}
}
}
},
{ headerName: 'Requested By', field: 'user_name', sortable: true, filter: 'agTextColumnFilter', tooltipField: 'user_name' },
{
headerName: 'Client', field: 'borrower_name', sortable: true, filter: 'agTextColumnFilter',
tooltipField: 'borrower_name', tooltipComponentParams: { color: "#ececec" }, width: 200
},
{
headerName: 'Debtor', field: 'customer_name', sortable: true, filter: 'agTextColumnFilter',
cellStyle: { color: 'blue', cursor: 'pointer' }, tooltipField: 'customer_name', width: 200
},
{
headerName: 'Current Limit', field: 'current_limit', sortable: true, filter: 'agNumberColumnFilter',
cellStyle: { textAlign: 'right' },
cellRenderer: this.CurrencyCellRenderer
},
{
headerName: 'Requested Limit', field: 'requested_limit', sortable: true, filter: 'agNumberColumnFilter',
cellStyle: { textAlign: 'right' },
cellRenderer: this.CurrencyCellRenderer
},
{
headerName: 'Approved Limit', field: 'approved_limit', sortable: true, filter: 'agNumberColumnFilter',
cellStyle: { textAlign: 'right' },
cellRenderer: this.CurrencyCellRenderer
},
{ headerName: 'Status', field: 'status', sortable: true, filter: 'agTextColumnFilter', width: 120, },
{ headerName: 'Comment', field: 'comment', sortable: true, filter: 'agTextColumnFilter', tooltipField: 'comment', width: 200 },
{
headerName: "",
suppressMenu: true,
suppressSorting: false,
cellClass: 'action-class',
width: 120,
template:
`<i class="fa fa-pencil-square-o" aria-hidden="true" data-action-type="view" pTooltip="Edit Queue" tooltipPosition="top"></i>
<i class="fa fa-info-circle" aria-hidden="true" data-action-type="history" pTooltip="View Comment History" tooltipPosition="top"></i>`
}
];
this.defaultColDef = {
enableValue: true,
sortable: true,
tooltipComponent: "customTooltip",
resizable: true
};
this.frameworkComponents = {
customTooltip: CustomTooltip,
agDateInput: CustomDateComponent };
Seems it's a custom tooltip issue (somewhere inside ag-grid), will try to inform the team and describe the bug.
In your case, you can specify your component (CustomDateComponent) directly via cellEditorFramework rather than cellEditor, without internal declaration in frameworkComponents block.
{
headerName: "Date",
field: "date",
width: 190,
editable:true,
cellEditorFramework:CustomDateComponent
}
Just for info: your CustomDateComponent not yet ready to be used as cellEditor

how to add conditional template on ag-grid

can I do a conditional template on the first column below?
for example:
If my row has score property and I want to hide the input when my score is above 70?
let columns = [
{ width: 30, suppressSorting: true, suppressMenu: true, template: '<input type="checkbox">' },
{ headerName: "Score", filter: 'number', valueGetter: (params : any) =>
params.data.traces ? (<Alert> params.data.traces[0]).severity : params.data.severity, width:70},
{ headerName: "Behaviour tags" },
{ headerName: "Host", field: "host_name" },
{ headerName: "Group Id", cellRenderer: 'group', width:140 },
{ headerName: "Comments",width:290 }
];
Use cellRenderer property in your column object
let columns = [{ width: 30, suppressSorting: true, suppressMenu: true,
cellRenderer: function (params) {
var display = 'block';
if (params.data.score > 70) {
display = 'none';
}
var html = '<input type="checkbox" style="display: ' + display + '">';
return html;
}
}]
In params.data you have all row data

Dojo-DataGrid :: How to dynamically fetch values as options for a select box in Dojo DataGrid

I have a Dojo-DataGrid which is programatically populated as below :
var jsonStore = new dojo.data.ItemFileWriteStore({ url: "json/gaskets.json" });
var layout= [
{ field: "description", width: "auto", name: "Tier/Description", editable:true },
{ field: "billingMethod", width: "auto", name: "Billing Method", editable: true,
type: dojox.grid.cells.Select, options: [ '0', '1' ] },
{ field: "offeringComponents", width: "auto", name: "Offering Component", editable: true,
type: dojox.grid.cells.Select, options: [ '0', '1' ] },
{ field: "serviceActivity", width: "auto", name: "Service Activity", editable: true,
type: dojox.grid.cells.Select, options: [ '0', '1' ] },
{ field: "hours", width: "auto", name: "Hours" },
{ field: "rate", width: "auto", name: "Rate <br/> (EUR)" },
{ field: "cost", width: "auto", name: "Cost <br/> (EUR)" },
{ field: "price", width: "auto", name: "Price <br/> (EUR)" },
{ field: "gvn", width: "auto", name: "Gvn" }
];
grid = new dojox.grid.DataGrid({
query: { description: '*' },
store: jsonStore,
structure: layout,
rowsPerPage: 20
}, 'gridNode');
The options for the field billingMethod (Currently defined as dojox.grid.cells.Select) are hard coded right now, but I would like to get those values dynamically from the backend as JSON. But dojox.grid.cells.Select currently(I am using Dojo 1.5) does not have a option to define a "store".
I am trying to use dijit.form.FilteringSelect, but this needs a id(of a Div) for its constructor and I cannot specify one as this select box has to go with in the grid, rather than a separate DIV.
Thanks
Sandeep
Your answer works fine, the issue is that in the combo the user can select A, but once the combo lose the focus, the value 1 will be shown. Some months ago I had the same problem, and I got a solution from KGF on #dojo. The idea is to have a formatter on the cell that just creates a SPAN element, and then it invokes a query over the store to get the label of the selected element and put it on the SPAN. I modified your example to get that working.
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dojox.grid.cells.dijit");
dojo.require("dojox.grid.DataGrid");
dojo.require("dijit.form.Select");
dojo.require('dojox.grid.cells.dijit');
dojo.require('dijit.form.FilteringSelect');
var grid;
var jsonStore;
dojo.addOnLoad(function() {
jsonStore = new dojo.data.ItemFileWriteStore({
data: {
"identifier": "identify",
"label": "description",
"items": [
{
"identify": 123,
"description": "Project Manager"},
{
"identify": 234,
"description": "Developer"},
{
"identify": 536,
"description": "Developer",
"billingMethod":2}
]
}
});
var myStore = new dojo.data.ItemFileReadStore({
data: {
identifier: 'value',
label: 'name',
items: [{
value: 1,
name: 'A',
label: 'A'},
{
value: 2,
name: 'B',
label: 'B'},
{
value: 3,
name: 'C',
label: 'C'}]
}
});
//[kgf] callback referenced by formatter for FilteringSelect cell
function displayValue(nodeId, store, attr, item) {
if (item != null) { //if it's null, it wasn't found!
dojo.byId(nodeId).innerHTML = store.getValue(item, attr);
}
}
var layout = [
{
field: "identify",
width: "auto",
name: "Id 2 Hide",
hidden: true},
{
field: "description",
width: "auto",
name: "Tier/Description",
editable: true},
{
field: 'billingMethod',
name: 'Billing Method',
editable: true,
required: true,
width: '150px',
type: dojox.grid.cells._Widget,
widgetClass: dijit.form.FilteringSelect,
widgetProps: {
store: myStore
},
formatter: function(data, rowIndex) { //[kgf]
//alert("data "+data)
var genId = 'title' + rowIndex;
var store = this.widgetProps.store;
var attr = "label";
setTimeout(function() {
store.fetchItemByIdentity({
identity: data,
onItem: dojo.partial(displayValue, genId, store, attr)
});
}, 50);
//for now return a span with a predetermined id for us to populate.
return '<span id="' + genId + '"></span>';
}
}
];
grid = new dojox.grid.DataGrid({
query: {
description: '*'
},
store: jsonStore,
singleClickEdit: true,
structure: layout,
rowsPerPage: 20
}, 'gridNode');
grid.startup();
});
I was finally able to figure this out..Incase someone wants to implement same kind of stuff using DOJO Datagrid+FilteringSelect.
Sample Code
dojo.require("dojo.data.ItemFileWriteStore");
dojo.require("dojo.data.ItemFileReadStore");
dojo.require("dojox.grid.cells.dijit");
dojo.require("dojox.grid.DataGrid");
dojo.require("dijit.form.Select");
dojo.require('dojox.grid.cells.dijit');
dojo.require('dijit.form.FilteringSelect');
var grid;
var jsonStore;
dojo.addOnLoad(function() {
jsonStore = new dojo.data.ItemFileWriteStore({
data: {
"identifier": "identify",
"label": "description",
"items": [
{
"identify": 123,
"description": "Project Manager"},
{
"identify": 234,
"description": "Developer"},
{
"identify": 536,
"description": "Developer"}
]
}
});
var myStore = new dojo.data.ItemFileReadStore({
data: {
identifier: 'value',
label: 'name',
items: [{
value: 1,
name: 'A',
label: 'A'},
{
value: 2,
name: 'B',
label: 'Y'},
{
value: 3,
name: 'C',
label: 'C'}]
}
});
var layout = [
{
field: "identify",
width: "auto",
name: "Id 2 Hide",
hidden: true},
{
field: "description",
width: "auto",
name: "Tier/Description",
editable: true},
{
field: 'billingMethod',
name: 'Billing Method',
editable: true,
required: true,
width: '150px',
type: dojox.grid.cells._Widget,
widgetClass: dijit.form.FilteringSelect,
widgetProps: {
store: myStore
}}
];
grid = new dojox.grid.DataGrid({
query: {
description: '*'
},
store: jsonStore,
singleClickEdit: true,
structure: layout,
rowsPerPage: 20
}, 'gridNode');
grid.startup();
});