Overview

The FlexJS DataGrid demonstrates the composition direction of FlexJS. The DataGrid is composed of a ButtonBar component (to act as the header row) and a set of List components for the columns. The lists are kept in a Container with a ScrollableViewport so they scroll together. The DataGrid keeps selections synchronized between the lists.

A typical DataGrid declaration in an application would be:

<js:DataGrid id="dataGrid" width="100%" height="100%" className="MyDataGrid" rowHeight="60">
	<js:beads>
		<js:ConstantBinding
				sourceID="applicationModel"
				sourcePropertyName="productList"
				destinationPropertyName="dataProvider" />
	</js:beads>
	<js:columns>
			<js:DataGridColumn label="First" dataField="image" itemRenderer="products.ImageItemRenderer"/>
			<js:DataGridColumn label="Second" dataField="title" />
			<js:DataGridColumn label="Third" dataField="sales"/>
		</js:columns>
</js:DataGrid>

The ConstantBinding bead associates the application model's productList data with the dataProvider property of the DataGridModel. Each column of the DataGrid is defined by the DataGridColumn element, giving a title to the column and identifying the field within the dataProvider to use for the column. Optionally, a DataGridColumn can specify a custom itemRenderer (the one here displays an image) and set a specific width (not shown) for the column.

In addition to the ConstantBinding bead, the DataGrid uses other beads internally, as identified by the DataGrid style declaration in the defaults.css file:

DataGrid
{
    IDataGridPresentationModel: ClassReference("org.apache.flex.html.beads.models.DataGridPresentationModel");
    IBeadView: ClassReference("org.apache.flex.html.beads.DataGridView");
    IBeadModel: ClassReference("org.apache.flex.html.beads.models.DataGridModel");
    IItemRendererClassFactory: ClassReference("org.apache.flex.core.ItemRendererClassFactory");
    IItemRenderer: ClassReference("org.apache.flex.html.supportClasses.StringItemRenderer");
    background-color: #FFFFFF;
}

IDataGridPresentationModel. This is not an actual "model" bead but does describe the data needed to present the DataGrid. This includes the labels for each column and the height of each row. Other presentation models could include column widths, sort order, or anything else you might need.

IBeadView: This bead assembles the user interface out of a ButtonBar and Lists (one per column). The DataGridPresentationModel's array of column labels is the dataProvider for the ButtonBar. A List is created for each of those labels and all of the Lists share the same dataProvider as the DataGrid.

IBeadModel: This bead contains the dataProvider used by the lists. Applications use a binding bead (e.g., ConstantBinding) to map the application's data model to the DataGridModel.

IItemRendererClassFactory: This bead is used to generated instances of the itemRenderer for each list. 

IItemRenderer: This bead names the default class to use for itemRenderers in the list. This can be overridden by the itemRenderer property of the DataGridColumn.

Adding DataGrid Beads

 

You can add beads to the DataGrid just like any other FlexJS component. Since the DataGrid does not come with a built-in grid lines, this example shows you how to make a bead to do that.
The bead is called, DataGridLinesBead, and has a property called, stroke, which specifies the color, alpha, and thickness of the grid lines. This particular bead draws both horizontal and vertical lines; you can modify it to do one or the other.

 

public function DataGridLinesBead()
{
	// Set default separator line stroke.
	var lineStroke:SolidColorStroke = new SolidColorStroke();
	lineStroke.color = 0x333333;
	lineStroke.alpha = 1.0;
	lineStroke.weight = 1;
    stroke = lineStroke;
}

 

Every bead follows a similar pattern:
  • The Constructor is called first. You can create additional elements here, but do not add them to the display list. You can also set default property values.
  • Properties set. Any properties that appear in MXML will be set at this point.
  • The strand setter is called. After a bead has been created and its properties set, the strand is given to the bead. You can also create UI elements here and add the elements created here (or in the Constructor) to the strand as its children. If your bead needs to respond to changes in the strand, such as changes to its size, set up event listeners for those events in the strand setter.
  • "beadsAdded" dispatched. Once a strand's beads have all been added and their strand setters called, the FlexJS framework dispatches the "beadsAdded" event on the strand. If you are writing a bead that needs to find or use other beads, set up an event listener for this event in the strand setter. When this event is called, you can continue setting up your bead and seek out other beads using the strand's getBeadByType() function.
  • Size change events. As the UI is built and layouts are run, the strand will be sized and dispatch events: "sizeChanged", "widthChanged", "heightChanged". One or more of these events may be sent and they may be sent at any time during the life of the strand and application.

 

Every bead must implement org.apache.flex.core.IBead interface which is where the strand gets set. For DataGridLinesBead, the strand setter function creates a GraphicsContainer where the lines will be drawn. The strand setter is also a good place to set up event listeners. This bead will want to know when the DataGrid's beads have been so it can attached the overlay to the Container housing the lists.

 

public function set strand(value:IStrand):void
{
	_strand = value;
	
	_overlay = new GraphicsContainer();
	
	IEventDispatcher(_strand).addEventListener("beadsAdded", handleBeadsAdded);
}

 

Once all of the DataGrid’s beads are added, the FlexJS framework dispatches a “beadsAdded” event which the DataGridLinesBead also listens for. In this event handler, the DataGridLinesBead finds the Container holding the lists which are the columns of the DataGrid. This container will be scrolled so the grid line overlay needs to be part of this Container, too.

 

private function handleBeadsAdded(event:Event):void
{
	var host:UIBase = _strand as UIBase;
	var n:int = host.numElements;
	for (var i:int=0; i < n; i++) {
		var child:UIBase = host.getElementAt(i) as UIBase;
		if (child.id == "dataGridListArea") {
			_area = child;
			_area.addElement(_overlay);
			break;
		}
	}
	
	// Now set up listeners to handle changes in the size of the DataGrid.
    IEventDispatcher(_strand).addEventListener("sizeChanged", drawLines);
	IEventDispatcher(_strand).addEventListener("widthChanged", drawLines);
	IEventDispatcher(_strand).addEventListener("heightChanged", drawLines);
	
	// Also set up a listener on the model to know when the dataProvider has
	// changed which might affect the number of rows/columns and thus the
	// grid lines.
	var model:IBeadModel = _strand.getBeadByType(IBeadModel) as IBeadModel;
	IEventDispatcher(model).addEventListener("dataProviderChanged", drawLines);
}

 

When it comes time to draw the lines, the process is straightforward: draw a vertical line between the columns and draw horizontal lines below each row. The function to draw the lines is the event handler for size and dataProvider changes.

 

private function drawLines(event:Event):void
{
	var sharedModel:IDataGridModel = _strand.getBeadByType(IBeadModel) as IDataGridModel;
	var presentationModel:DataGridPresentationModel = _strand.getBeadByType(DataGridPresentationModel) as DataGridPresentationModel;
	var layoutParent:ILayoutHost = _area.getBeadByType(ILayoutHost) as ILayoutHost;
	var contentView:IParentIUIBase = layoutParent.contentView as IParentIUIBase;
			
	var columns:Array = sharedModel.columns;			
	var arrayList:ArrayList = sharedModel.dataProvider as ArrayList;
	var rowHeight:Number = presentationModel.rowHeight;
			
	var totalHeight:Number = arrayList.length * rowHeight;
	var columnWidth:Number = _area.width / columns.length;
			
	// translate the stroke to a fill since rectangles are used for the grid
	// lines and not lines.
	var lineFill:SolidColor = new SolidColor();
	var weight:Number = 1;
	lineFill.color = (stroke as SolidColorStroke).color;
	lineFill.alpha = (stroke as SolidColorStroke).alpha;
	weight = (stroke as SolidColorStroke).weight;
	_overlay.fill = lineFill;
			
	columnWidth = (columns[0] as DataGridColumn).columnWidth;
	var xpos:Number = isNaN(columnWidth) ? _area.width / columns.length : columnWidth;
			
	_overlay.removeAllElements();
			
	// draw the verticals
	for (var i:int=1; i < columns.length; i++) {
		_overlay.drawRect(xpos, 0, weight, totalHeight);
		columnWidth = (columns[i] as DataGridColumn).columnWidth;
		xpos += isNaN(columnWidth) ? _area.width / columns.length : columnWidth;
	}
			
	var n:int = arrayList.length;
			
	// draw the horizontals
	for (i=1; i < n+1; i++) {
		_overlay.drawRect(0, i*rowHeight, _area.width, weight);
	}
}

 

Once the bead is completed it can be added to the beads for the DataGrid in the application MXML (shown below, setting the stroke to a solid red line):

 

<js:DataGrid id="dataGrid" width="100%" height="100%" className="MyDataGrid" rowHeight="60">
	<js:beads>
		<beads1:DataGridLinesBead>
            <beads1:stroke>
                 <js:SolidColorStroke color="#FF0000" />
            </beads1:stroke>
        </beads1:DataGridLinesBead>
		<js:DataProviderChangeNotifier sourceID="applicationModel" propertyName="productList" />
		<js:ConstantBinding
			sourceID="applicationModel"
			sourcePropertyName="productList"
			destinationPropertyName="dataProvider" />
	</js:beads>
	<js:columns>
		<js:DataGridColumn label="First" dataField="image" itemRenderer="products.ImageItemRenderer"/>
		<js:DataGridColumn label="Second" dataField="title" />
		<js:DataGridColumn label="Third" dataField="sales"/>
	</js:columns>
</js:DataGrid>

 

 

 

  • No labels