Hi all (again), I guess using an existing post to create a new one simply by changing the subject wasn't a good idea. It got stuck under a completely unrelated thread; So, sorry for the double post, but at least now I will know that this isn't buried and ignored by accident...Here's my original post: -------------------------------------------------------------------- I'm pretty sure that any answers for this one will help a lot of people, since the basis of it crosses many complex business applications.
I have existing CF apps (Purchase Requests, Travel Requests, Request Approvals, etc.) that contain many-to-many relationships, such as PR Number > Charge Strings > Item/Costs, wherein the PR contains one or more charge strings, and each charge string can have one or more items. I am prototyping Flex versions, but I have come across a little (ha) bit of a situation where I am not sure of the best method to proceed. I have a actionscript form for creating a _new_ request, allowing the user to add charge strings and associated items dynamically (stripped version included below in case someone needs/wants to see how I did this). Textually, a request being updated might look like this: Request 1001 Charge String 1: CS: (combobox) Item 1: Type (combobox) Cost (Number)...etc. Item 2: Type (combobox) Cost (Number)...etc. Item 3: Type (combobox) Cost (Number)...etc. Charge String 2: (combobox) Item 1: Type (combobox) Cost (Number)...etc. Item 2: Type (combobox) Cost (Number)...etc. Charge String 3: (combobox) Item 1: Type (combobox) Cost (Number)...etc. Note that Charge Strings and Items have unique IDs and there is a relation between items and charge strings as well as items and the request. However, a request also has to be available for updates, since they can remain in draft status before being submitted for approvals. The situation is this: Because of the possible many-to-many relationship between charge strings and related items within a request, I'm not quite sure of the best method of populating the form for updates. Additionally, the user may add, remove or update items or an existing charge string, and add new charge strings with new items In CF, I only have to make 1 query to get the primary data I need, and I can do a nested loop to create the form easily by checking for a change in the charge string to know when to display the CS combobox and when to display it's associated items. On update, we archive the old charge string and item data and insert new rows into SQL Server, rather than trying to determine which charge strings and/or items were updated. This is for processing speed and the ability to recreate a request at any point in time (using version numbers), the latter being necessary for auditing the approval/rejection history of a request. I am not sure if looping is the best method to use in Flex -- particularly since the containers are dynamic out of necessity -- nor am I positive about the manner in which I should do this, in particular the data population/combobox selected items. (Btw, I started doing this with components, but actionscript was faster for me to get it to work...I'll end up going back to that just for code reuse but I like to see things work first :-) ----- charge string with estimated cost items code module (improvements or hints appreciated as well) ----- <?xml version="1.0" ?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml <http://www.adobe.com/2006/mxml>" creationComplete="addCS();"> <mx:Script> <![CDATA[ import mx.controls.Alert; import mx.controls.TextInput; import mx.containers.HBox; import mx.controls.ComboBox; import flash.events.Event; import flash.events.MouseEvent; import mx.collections.ArrayCollection; import mx.utils.ArrayUtil; //sample data for charge string combobox; would normally come from an RO call [Bindable] public var CSCollection:ArrayCollection = new ArrayCollection([{csUID: 1000, label: "CS1000"},{csUID: 1001, label: "CS1001"},{csUID: 1002, label: "CS1002"}]); //sample data for estimate type combobox; would normally come from an RO call [Bindable] public var ETCollection:ArrayCollection = new ArrayCollection([{etUID: 1000, label: 'Airfare'},{etUID: 1001, label: 'Meals'},{etUID: 1002, label: 'Other'}]); public function addCS():void { var newVBox:VBox = new VBox(); newVBox.setStyle( "borderColor", "#000000" ); newVBox.setStyle( "borderStyle", "solid" ); newVBox.setStyle("paddingBottom", 5); newVBox.setStyle("paddingRight", 5); newVBox.setStyle("paddingLeft", 5); newVBox.setStyle("paddingTop", 5); newVBox.percentWidth = 100; var newCS:ComboBox = new ComboBox(); newCS.percentWidth = 100; newCS.dataProvider = CSCollection; var csCloseBtn:Button = new Button(); var csAddEstBtn:Button = new Button(); csCloseBtn.label = "Delete this Charge String and Associated Estimates"; csCloseBtn.addEventListener(MouseEvent.CLICK,csCloseBtnClickListener); csAddEstBtn.label = "Add Another Estimate to this Charge String"; csAddEstBtn.addEventListener(MouseEvent.CLICK,csAddEstBtnClickListener); var newbtnHbox3:HBox = new HBox(); newbtnHbox3.addChild(newCS); newbtnHbox3.addChild(csCloseBtn); newbtnHbox3.addChild(csAddEstBtn); newVBox.addChild(newbtnHbox3); var eparent:Object = newVBox; addEst(eparent); csbox.addChild(newVBox); } public function csCloseBtnClickListener(e:Event):void { var ncs:Number = csbox.numChildren; if (ncs != 1) { csbox.removeChild(e.target.parent.parent); } else { Alert.show("Minimum of One Charge String Required"); } } public function csAddEstBtnClickListener(e:Event):void { addEst(e.target.parent.parent); } public function estCloseBtnClickListener(e:Event):void { //set a var indicating the hbox that is the estimate row object... var thisestChild:DisplayObject = e.target.parent.parent; //set a var indicating the parent csbox that contains the estimate row object var thiscsboxDO:Object = e.target.parent.parent.parent; //determine number of estimates: total children in csbox - 1 cs & button row var nest:Number = e.target.parent.parent.parent.numChildren - 1; if (nest != 1) { thiscsboxDO.removeChild(thisestChild); } else { Alert.show("Minimum of One Estimate per Charge String Required"); } } public function addEst(eparent:Object):void { var newVbox2:VBox = new VBox(); newVbox2.percentWidth = 50; var newEst2 : TextInput = new TextInput(); newEst2.text = "(estimated cost here)" var newcbEst:ComboBox = new ComboBox(); newcbEst.dataProvider = ETCollection; var estCloseBtn:Button = new Button(); estCloseBtn.label = "Delete this Estimate"; estCloseBtn.addEventListener(MouseEvent.CLICK,estCloseBtnClickListener); var newHbox3:HBox = new HBox(); newHbox3.addChild(newcbEst); newHbox3.addChild(newEst2); newHbox3.addChild(estCloseBtn); newVbox2.addChild(newHbox3); eparent.addChild(newVbox2); } ]]> </mx:Script> <mx:Canvas height="100%" width="100%" id="csestcanvas"> <mx:Panel title="Charge Strings and Estimated Costs" id="CSEstPanel" backgroundColor="#eeeeee" width="90%" height="{csestcanvas.height}" paddingTop="10" paddingLeft="10" paddingRight="10" paddingBottom="10"> <mx:Button id="AddCS" label="Add a New Charge String" click="addCS()"/> <mx:VBox width="100%" id="csbox" > <!-- this is where all AS containers/controls are added --> </mx:VBox> </mx:Panel> </mx:Canvas> </mx:Application>