Use RecordsView Control
RecordsView control is a grid-like UI element useful for displaying records of data. It is usually used to display results of the Query select() function.
To initialize RecordsView control, RecordsView.source property must be set. Value can be a records object, an array of records, or an array of attributes.
Initialize With Records
The main job for RecordsView control is to display one or more records collections.
var contracts = await Contract.where(...).selectAsync();
recView.source = contracts;
var contracts = await Contract.where(...).selectAsync();
var contractors = await Contractor.where(...).selectAsync();
recView.source = [contracts, contractors];
RecordsView With Sub-Records Callback
In the case you want your RecordsView to have additional level that is populated when parent row is expanded, you should implement RecordsView.subrecords event handler.
this.customersView.subrecords( async (r, idx)=> {
return await Contacts.where(Contacts.partyB, WhereOperator.Equal, r.id).selectAsync();
});
var groups = Grantee.where(Grantee.type, WhereOperator.Equal, 2/*group*/).select();
// this is equivalent to Management.groups
this.recordsView = new RecordsView(this);
this.recordsView.align = Align.Client;
this.recordsView.options.gridLines = true;
this.recordsView.options.header = false;
this.recordsView.fields = [
Grantee.name, Grantee.label, // 1st level
[Grantee.name, Grantee.label] // 2nd level
];
this.recordsView.subrecords((group) => group.members);
this.recordsView.source = groups;

Initialize With Attributes
We can also use attributes to initialize RecordsView. Each control's row is one attributes object or array of attribute.
recView.source = [
[atrA, atrB, [subAtr1, subAtr2, subAtr3]] // 1st row with 3 details
[atrC, atrD, [subAtr4, subAtr5]] // 1nd row with 2 details
];
Fields
Sometimes we want to choose which field from the records collection we want to show in RecordsView.
// simple
recView.fields = [Contract.party, Contract.amount];
// with details
recView.fields = [Contract.party, Contract.amount, [Contractor.name]];
// with Attribute objects
recView.fields = [
new Attribute({
name: Contract.amount, // could use a simple text too
type: AttributeValueType.String, // Contract.amount is numeric, but we need string
value: (r)=> r.amount + ' $', // use getter to format display
setValue: (r, v) => { // use setter to process value
Record.amount = do some calc with v;
},
foreground: (r)=> { // draw RED all > 1000
if (r.amount >= 1000) return 'red';
},
font: { style: { bold: true, underline: true } }
}),
new Attribute({
name: Contract.party,
}),
];
Override Captions
Defines captions (headers) for the columns in the records view.
// simple
recView.captions = ['Contract Party', 'Amount'];
// with detail
recView.captions = ['Contract Party', 'Amount', ['SubLevelCol1']];
Sorting
Defines the sorting of the records displayed in the RecordsView.
// simple
recView.sorting = [new Sorting(Contract.party, SortingOrder.Asc)];
// equivalent ('Asc' is default)
recView.sorting = [Contract.party]
// equivalent (using dict)
recView.sorting = [{field: Contract.party, sorting: SortingOrder.Asc}];
Grouping
Specifies how the records should be grouped in the RecordsView.
// if recordView has multiple tabs
this.recordView.grouping = [
[Contract.partyA, Contract.date], // 1st tab grouping
[Customer.name] // 2nd tab grouping
];
// if recordView has details with details
this.recordView.grouping = [
Contract.partyA, // 1st level grouping
[Note.date] // detail level grouping
];
Summary
Defines sums for the columns in the RecordsView. See Summary.
this.recordView.summary = [new Summary(Contract.amount, SummaryKind.Sum, SummaryPosition.Footer, '$,0.00')];
Filter
Specifies filtering criteria to limit the records shown in the RecordsView. Please note that this filter is applied on local data. See Filter.