Working with data models

In this section, we will be describe a basic scenario for data model integration. Let's consider a simple scenario where a process model is described in a graph and where the nodes data is managed by the application where Diagrammer is embedded. The data of each node represents a data model consumed by the nodes renderer and a data grid displaying its content. Customizing a renderer can be done by binding the content of the data model and editing the nodes directly or via the dataGrid.
The graph is generated from a GraphML input. For each node id, a data model is created (in enterprise applications, this data will be acquired from the application context) and injected at the VisualizerEvent.DATA_LOADED event. The dataModel contains a label value to be displayed (textValue property) and a type value to control the renderer shape (typeValue property). The goal is to maintain the data model (content and visual representation) both in Diagrammer and the DataGrid (simulating the application context in this case).

First, we will create the application : its graph, its data model, data grid and some event handlers. In the application, the data grid modifies directly the data model by editing its properties. The Diagrammer nodes should respond to such modification via binding. The data grid should respond to some nodes content edition. The following code represents the implementation of the scenario described above.

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" 
			   xmlns:diagrammer="fr.kapit.diagrammer.*" 
			   xmlns:components="fr.kapit.diagrammer.artifacts.components.*" 
			   xmlns:data="dataModel.data.*"
			   minWidth="955" minHeight="600"
			   creationComplete="application_creationCompleteHandler(event)" xmlns:styles="fr.kapit.visualizer.styles.*"
			   >
	<fx:Script>
		<![CDATA[
			import dataModel.renderers.ProcessRenderer;
			import fr.kapit.diagrammer.base.IDiagramSprite;
			import fr.kapit.visualizer.base.IItem;
			import fr.kapit.visualizer.events.VisualizerEvent;
			import mx.collections.ArrayCollection;
			import mx.controls.TextInput;
			import mx.events.DataGridEvent;
			import mx.events.FlexEvent;
			private var dataModelMap:Dictionary;
			[Bindable]
			private var dataModelArray:ArrayCollection;
			
			protected function application_creationCompleteHandler(event:FlexEvent):void
			{
				dataModelMap = new Dictionary();
				dataModelArray = new ArrayCollection();
				addModelUnit(dataProvProcess,'n1');
				addModelUnit(itemRendrProcess,'n2');
				addModelUnit(dataModelProcess,'n3');
				addModelUnit(readDataProcess,'n4');
				addModelUnit(creatRenderProcess,'n5');
				addModelUnit(isIEditableProcess,'n6');
				addModelUnit(affectRenderProcess,'n7');
				addModelUnit(updateRenderProcess,'n8');
				addModelUnit(dataModelChanges,'n9');
				addModelUnit(renderGraphProcess,'n10');
			}
			protected function addModelUnit(model:ProcessDataModel, id:String):void
			{
				dataModelMap[id] = model;
				dataModelArray.addItem(model);
			}
			protected function diagrammer_elementsCreatedHandler(event:VisualizerEvent):void
			{
				var item:IDiagramSprite = event.items[0];
				if(item)
					item.dataModel = dataModelMap[item.itemID];
			}
			protected function diagrammer_dataLoadedHandler(event:VisualizerEvent):void
			{
				var item:IDiagramSprite;
				var o:ProcessDataModel;
				for each(item in diagrammer.nodesMap)
				{
					o =  dataModelMap[item.itemID]
					item.dataModel = o;
					item.dataModelID = item.itemID;
				}
			}
			protected function datagrid_itemEditEndHandler(event:DataGridEvent):void
			{
				var item:ProcessDataModel = event.itemRenderer.data as ProcessDataModel;
				var txtControl:TextInput= event.currentTarget.itemEditorInstance as TextInput;
				item[event.dataField] = txtControl.text;
			}

		]]>
	</fx:Script>
	<fx:Declarations>
		<data:ProcessDataModel id="dataProvProcess" typeValue="process" textValue="Define Data Provider"/>
		<data:ProcessDataModel id="itemRendrProcess" typeValue="process" textValue="Define Item Renderer"/>
		<data:ProcessDataModel id="dataModelProcess" typeValue="optional process" textValue="Preparing Data Model"/>
		<data:ProcessDataModel id="readDataProcess" typeValue="process" textValue="Parsing Data"/>
		<data:ProcessDataModel id="creatRenderProcess" typeValue="process" textValue="Creating Renderers"/>
		<data:ProcessDataModel id="isIEditableProcess" typeValue="condition" textValue="Renderer is IEditable"/>
		<data:ProcessDataModel id="affectRenderProcess" typeValue="process" textValue="Assign Data Model to each renderer"/>
		<data:ProcessDataModel id="updateRenderProcess" typeValue="process" textValue="Update Renderers"/>
		<data:ProcessDataModel id="dataModelChanges" typeValue="event" textValue="Data Model Changes"/>
		<data:ProcessDataModel id="renderGraphProcess" typeValue="event" textValue="Renderer Graph"/>
		<fx:XML id="dataProvider" xmlns="">
			<graphml>
				<node id='n1' color='red'/>
				<node id='n2' color='red'/>
				<node id='n3' color='green'/>
				<node id='n4' color='red'/>
				<node id='n5' color='red'/>
				<node id='n6' color='blue'/>
				<node id='n7' color='red'/>
				<node id='n8' color='red'/>
				<node id='n9' color='red'/>
				<node id='n10' color='yellow'/>
				<edge id="e1" source="n1" target="n4"/>
				<edge id="e2" source="n2" target="n4"/>
				<edge id="e3" source="n3" target="n4"/>
				<edge id="e4" source="n4" target="n5"/>
				<edge id="e5" source="n5" target="n6"/>
				<edge id="e6" source="n5" target="n9"/>
				<edge id="e7" source="n6" target="n7"/>
				<edge id="e8" source="n7" target="n8"/>
				<edge id="e9" source="n8" target="n9"/>
				<edge id="e10" source="n10" target="n8"/>
				
			</graphml>
		</fx:XML>
		<styles:LinkStyle id="linkStyle" arrowTargetType="arrow" arrowSourceType="circle"/>
	</fx:Declarations>
	<s:HGroup width="100%" height="100%">
		<diagrammer:Diagrammer id="diagrammer" 
							   width="100%" 
							   height="100%"
							   layout="hierarchicalCyclic"
							   dataProvider="{dataProvider}"
							   nodeRendererClass="{ProcessRenderer}"
							   dataLoaded="diagrammer_dataLoadedHandler(event)"
							   linkStyle="{linkStyle}"
							   />
	</s:HGroup>
	<mx:DataGrid width="300" height="200" dataProvider="{dataModelArray}" editable="true" itemEditEnd="datagrid_itemEditEndHandler(event)">
		<mx:columns>
			<mx:DataGridColumn dataField="textValue" headerText="Text"/>
			<mx:DataGridColumn dataField="typeValue" headerText="Type"/>
		</mx:columns>
	</mx:DataGrid>
	
</s:Application>


Once the data model is injected in Diagrammer nodes, it is mandatory to use the adequate renderer. It should: Below is an implementation of such a renderer. Note thzt the bidirectional binding of the data model properties and the renderer properties which enables synchronization between the data grid elements and Diagrammer nodes:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:fx="http://ns.adobe.com/mxml/2009" 
		   xmlns:s="library://ns.adobe.com/flex/spark" 
		   xmlns:mx="library://ns.adobe.com/flex/mx" 
		   width="200" height="50"
		   backgroundColor="{colorValue}" cornerRadius="{roundRadius}"
		   borderColor="0x151515" borderAlpha="1" borderVisible="true"
		   horizontalScrollPolicy="off" verticalScrollPolicy="off"
		   implements="fr.kapit.visualizer.renderers.IRenderer, fr.kapit.diagrammer.renderers.IEditable"
		   rollOver="renderer_rollOverHandler(event)"
		   rollOut="renderer_rollOutHandler(event)"
		   >
	<fx:Script>
		<![CDATA[
			import dataModel.data.ProcessDataModel;
			
			import fr.kapit.diagrammer.renderers.IEditable;
			import fr.kapit.visualizer.base.IItem;
			import fr.kapit.visualizer.renderers.IRenderer;
			
			import mx.events.FlexEvent;
			
			import spark.events.TextOperationEvent;
			import spark.skins.spark.ComboBoxTextInputSkin;
			[Bindable]
			public var textValue:String;
			[Bindable]
			public var colorValue:uint=0xffffff;
			[Bindable]
			private var roundRadius:uint=0;
			[Bindable]
			private var _typeValue:String;
			
			[Bindable]
			public function get typeValue():String
			{
				return _typeValue;
			}
			public function set typeValue(value:String):void
			{
				_typeValue=value;
				switch(value)
				{
					case 'process' : roundRadius = 0;break;
					case 'optional process' : roundRadius = 10;break;
					case 'condition' : roundRadius = Math.max(height,width)*0.5;break;
					default : roundRadius = 0;
				}
			}
			private var _data:Object;	
			public override function get data():Object
			{
				return _data;
			}
			public override function set data(value:Object):void
			{
				_data = value;
				if(!colorMap)
					initColors();
				colorValue = colorMap[value.color];
				typeValue = value.shape;
			}
			private var _item:IItem;
			public function get item():IItem
			{
				return _item;
			}
			public function set item(value:IItem):void
			{
				_item = value;
			}	
			public function get isFixed():Boolean
			{
				return false;
			} 
			public function set isFixed(value:Boolean):void {;}
			
			[Bindable]
			private var _dataModel:ProcessDataModel;
			public function set dataModel(value:Object):void
			{
				_dataModel=value as ProcessDataModel;
				if(!_dataModel)
					return;
				textValue = _dataModel.textValue;
				typeValue = _dataModel.typeValue;
			}
			public function get dataModel():Object
			{
				return _dataModel;
			}
			public function get limitWidth():Number
			{
				return 50;
			}
			public function get limitHeight():Number
			{
				return 50;
			}
			public function get isSizeFixed():Boolean
			{
				return false;
			}
			public function set isSizeFixed(value:Boolean):void {;}
			public function get prohibitLinkingFrom():Boolean
			{
				return false;
			}
			public function set prohibitLinkingFrom(value:Boolean):void {;}
			public function get prohibitLinkingTo():Boolean
			{
				return false;
			}
			public function set prohibitLinkingTo(value:Boolean):void {;}
			
			private var colorMap:Dictionary;
			protected function initColors():void
			{
				colorMap=new Dictionary();
				colorMap['red'] = 0x55f9f9;
				colorMap['blue'] = 0xf9f955;
				colorMap['green'] = 0xf955f9;
				colorMap['yellow'] = 0x5555f9;
			}
			private function updateRendererSize():void
			{
				validateNow();
				var rect:Rectangle = getBounds(this);
				width = rect.width;
				height = rect.height;
			}
			/* ****************
			 *  Event Handlers
			 ******************/
			protected function textinput_changeHandler(event:TextOperationEvent):void
			{
				textValue = textInput.text;
			}
			protected function renderer_rollOverHandler(event:MouseEvent):void
			{
				currentState = 'editing';
			}
			protected function renderer_rollOutHandler(event:MouseEvent):void
			{
				currentState = 'idle';
				updateRendererSize();
			}

		]]>
	</fx:Script>
	<fx:Declarations>
		
	</fx:Declarations>
	<mx:states>
			<s:State name="idle"/>
			<s:State name="editing"/>
	</mx:states>
	<fx:Binding twoWay="true" source="_dataModel.textValue" destination="textValue"/>
	<fx:Binding twoWay="true" source="_dataModel.typeValue" destination="typeValue"/>
	<mx:HBox paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5" 
			 width="100%" height="100%" 
			 verticalAlign="middle" horizontalAlign="center" includeIn="editing">
		<s:TextInput id="textInput" text="{textValue}" change="textinput_changeHandler(event)" width="100%" height="100%" textAlign="center"/>
	</mx:HBox>	
	<mx:HBox paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5" 
			 width="100%" height="100%" 
			 verticalAlign="middle" horizontalAlign="center" includeIn="idle">
		<s:Label id="textLabel" text="{textValue}"/>
	</mx:HBox>
</mx:Canvas>