As part of the Angular 2 custom component series, today we’re looking into how to create a custom component using aggregation.
Here’s a brief roadmap:
Add wj-flex-grid component to our component template.
Let’s look at the last item first. How do we specify arbitrary columns for our aggregated-grid component in markup where it’s needed? We could use wj-flex-grid-column components for this, in the same way as we do it with WjFlexGrid and our custom InheritedGrid. But the problem is that wj-flex-grid-column won’t work this way—it requires a WjFlexGrid or derived class component as its parent, and it can’t work with an unknown parent component.
The better answer is to create a custom component representing a column definition for our AggregatedGrid component.
We’ll create an AggregatedGridColumn component (with the aggregated-grid-column element name) that—since it’s nested in the aggregated-grid element—will define a column definition for it. We’ll suppose multiple aggregated-grid-column components inside a single aggregated-grid.
Here’s an example of the aggregated-grid markup with nested aggregated-grid-column components:
[itemsSource]="data"
[showSelectColumn]="false"
[selectionType]="selectionType"
style="height:300px;display:block;width:auto">
[binding]="'id'"
[width]="80">
[header]="'Date'"
[width]="150"
[cellTemplate]="editableDateRenderer">
[header]="'Country'"
[cellTemplate]="editableStringRenderer">
The AggregatedGrid component will collect child AggregatedGridColumn**(s) and generate corresponding wj-flex-grid-column components inside its host wj-flex-grid**.
In most cases, AggregatedGridColumn should have all the same properties as WjFlexGridColumn available for binding:
import { Component, Type, ComponentMetadata} from 'angular2/core';
import * as wjNg2Grid from 'wijmo/wijmo.angular2.grid';
import * as wjBase from 'wijmo/wijmo.angular2.directiveBase';
@Component({
selector: 'aggregated-grid-column',
template: '',
inputs: (wjBase.Ng2Utils.getTypeAnnotation(
wjNg2Grid.WjFlexGridColumn, ComponentMetadata))
.inputs.concat('cellTemplate')
})
export class AggregatedGridColumn {
// Defines a type of a component that should be used as the column cell template.
cellTemplate: Type;
};
Concise and easy, isn’t it? The interesting thing here is how we create the decorator’s inputs array:
The cellTemplate property we added here accepts a type of the Angular component that will be used to render the cells in the column specified. In our application, we form all the cell templates as components, which makes them easily reusable in different places of the application. Because of this, we don’t need to implement a support for defining the column’s cell template with an arbitrary nested HTML markup (which is a non-trivial task by itself). We only need a property holding a reference to cell template’s component type.
Here’s the class implementation:
@Component({
directives: [wjNg2Grid.WjFlexGrid, wjNg2Grid.WjFlexGridColumn, wjNg2Grid.WjFlexGridCellTemplate,
wjCore.WjComponentLoader, EditableSelectionRenderer],
selector: 'aggregated-grid',
templateUrl: 'src/customizedComponents/aggregatedGrid.html'
})
export class AggregatedGrid {
private _isEditable = true;
// grid data source
@Input() itemsSource: any;
// A type of selection provided by the Select column.
@Input() selectionType = SelectionType.Single;
// References SelectionType enum to use it in markup.
SelectionTypeEnum = SelectionType;
// References aggregated FlexGrid instance
@ViewChild('flex') flex: wijmo.grid.FlexGrid;
// A collection of column definitions.
@ContentChildren(forwardRef(() => AggregatedGridColumn))
columns: QueryList;
onFormatItem: (e: wijmo.grid.FormatItemEventArgs) => void;
constructor() {
// Provide correct 'this' for the formatItem event handler.
this.onFormatItem = this._onFormatItem.bind(this);
}
// Indicates whether grid cells editing is enabled.
@Input()
get isEditable(): boolean {
return this._isEditable;
}
set isEditable(value: boolean) {
if (this._isEditable != value) {
this._isEditable = value;
if (this.flex) {
// invalidates grid to apply changes
this.flex.invalidate();
}
}
}
// FlexGrid.formatItem event handler, enables or disables cell editing
// based on the isEditable property value.
private _onFormatItem(e: wijmo.grid.FormatItemEventArgs) {
if (e.panel.cellType === wijmo.grid.CellType.Cell) {
let column = this.flex.columns[e.col];
wijmo.enable(e.cell, this.isEditable || column.name === 'select');
}
}
};
Because our component is created from scratch, not inherited from some class, it has only three properties: itemsSource that specifies the aggregated WjFlexGrid data source, and selectionType and isEditable, which have the same semantics like in the InheritedGrid class.
Everything we need to gather the child AggregatedGridColumn components is in the following columns property declaration decorated by the @ContentChildren query:
@ContentChildren(forwardRef(() => AggregatedGridColumn))
columns: QueryList;
Angular will automatically fill the columns property with a collection of child AggregatedGridColumn components. To add these columns to the aggregated wj-flex-grid, we use the ngFor directive bound to the columns property to generate corresponding wj-flex-grid-column directives in the component template:
Let’s discover the component template in more detail. Here’s how it looks:
[itemsSource]="itemsSource"
[selectionMode]="'None'"
[isReadOnly]="true"
(formatItem)="onFormatItem($event)"
style="height:100%">
[binding]="'active'"
[name]="'select'">
[selectionType]="selectionType">
[header]="col.header"
[binding]="col.binding"
[name]="col.name"
[width]="col.width">
[cellType]="'Cell'" #cell="cell">
[properties]="{cell: cell}">
The root of the template is the wj-flex-grid component that causes our custom component to look and behave like a grid. Here’s a walkthrough:
We considered two key ways to create customized Wijmo Angular 2 components: by inheriting from the component class, and by aggregating it in the custom component. Both approaches have their pros and cons, and selection depends on specifics of the component you plan to create.
The working version of the sample that was used to explain these techniques can be found here:
http://demos.wijmo.com/5/SampleExplorer/SampleExplorer/Sample/CustomizedComponents
It’s also included in the Wijmo distribution zip file, which you can find in the following folder:
Samples\TS\Angular2\CustomizedComponents