Flextrine Tutorial – CRUD in a simple Flex 4 address book: Updating entities

Introduction
Setting up the server
Creating the entities
Creating the database schema
Loading the entities
Creating new entities
Deleting entities
Updating entities
Conclusion

Once an entity is in an EntityRepository (either because it was loaded from the database or it was persisted) it is known as a MANAGED entity.  This means that Flextrine will constantly watch the entity to see if anything changes, and if so will mark the entity for updating on the next flush().

Therefore updating entities is extremely simple – as long as the entity is managed there is literally nothing to do 🙂

Lets create an editor for the ContactGroups (in the same directory as Main.mxml):

ContactGroupEditor.mxml

   1: <?xml version="1.0" encoding="utf-8"?>

   2: <s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"

   3:          xmlns:s="library://ns.adobe.com/flex/spark"

   4:          xmlns:mx="library://ns.adobe.com/flex/mx">

   5:     

   6:     <fx:Script>

   7:         <![CDATA[

   8:         import vo.ContactGroup;

   9:         

  10:         [Bindable]

  11:         public var contactGroup:ContactGroup;

  12:         

  13:         ]]>

  14:     </fx:Script>

  15:     

  16:     <mx:Form labelWidth="150" width="100%" height="100%">

  17:         

  18:         <mx:FormHeading label="Edit group" />

  19:         

  20:         <mx:FormItem label="Name">

  21:             <s:TextInput text="@{contactGroup.name}" />

  22:         </mx:FormItem>

  23:         

  24:     </mx:Form>

  25:     

  26: </s:Group>

The component takes a contactGroup and provides a TextInput that edits its name attribute using Flex 4 two-way databinding.  In fact you need to be careful with two-way databinding on some components (e.g. DateChooser) as it can cause unnecessary updates, but it works fine for Spark’s TextInput.

Now we need to add the ContactGroupEditor into Main.mxml:

   1: ...

   2: <s:VGroup width="100%" height="100%">

   3:             <!-- The editors -->

   4:             <local:ContactGroupEditor contactGroup="{tree.selectedItem as ContactGroup}" enabled="{tree.selectedItem is ContactGroup}" />

   5: ...

And that’s it for the group editor.  When we select a ContactGroup in the tree, the ContactGroupEditor becomes enabled, and the selected item is passed to the editor’s contactGroup attribute.  Flex updates the contactGroup’s name attribute when we change it in the editor and Flextrine automatically picks up on the change and marks the entity for updating.  On the next flush() the changes will be written to the database.

Now for the ContactEditor.

ContactEditor.mxml

   1: <?xml version="1.0" encoding="utf-8"?>

   2: <s:Group xmlns:fx="http://ns.adobe.com/mxml/2009"

   3:          xmlns:s="library://ns.adobe.com/flex/spark"

   4:          xmlns:mx="library://ns.adobe.com/flex/mx">

   5:     

   6:     <fx:Script>

   7:         <![CDATA[

   8:         import org.davekeen.flextrine.orm.EntityManager;

   9:         import vo.ContactGroup;

  10:         import vo.Contact;

  11:         

  12:         [Bindable]

  13:         public var contact:Contact;

  14:         

  15:         ]]>

  16:     </fx:Script>

  17:     

  18:     <mx:Form labelWidth="150" width="100%" height="100%">

  19:         

  20:         <mx:FormHeading label="Edit contact" />

  21:         

  22:         <mx:FormItem label="Name">

  23:             <s:TextInput text="@{contact.name}" />

  24:         </mx:FormItem>

  25:         

  26:         <mx:FormItem label="Telephone number">

  27:             <s:TextInput text="@{contact.telephoneNumber}" />

  28:         </mx:FormItem>

  29:         

  30:         <mx:FormItem label="Birthday">

  31:             <mx:DateChooser selectedDate="{contact.birthday}" change="contact.birthday = event.currentTarget.selectedDate" />

  32:         </mx:FormItem>

  33:         

  34:         <mx:FormItem label="Group">

  35:             <s:DropDownList dataProvider="{EntityManager.getInstance().getRepository(ContactGroup).entities}"

  36:                             selectedItem="{contact.contactGroup}"

  37:                             change="contact.setContactGroup(event.currentTarget.selectedItem);"

  38:                             labelField="name" />

  39:         </mx:FormItem>

  40:         

  41:     </mx:Form>

  42:     

  43: </s:Group>

The contact editor is marginally more complicated, but still fairly simple.  A few points:

  • Note is that as mentioned above we don’t use two-way databinding on any component apart from TextInput.
  • Although it is perfectly legal to retrieve the singleton EntityManager in the ContactEditor as we have done in the DropDownList dataProvider, in a real application we probably wouldn’t do this as it breaks encapsulation.  I’ve done it here for the sake of simplicity 🙂

Finally we need to add the editor to Main.mxml:

   1: ...

   2:  

   3: <s:VGroup width="100%" height="100%">

   4:     <!-- The editors -->

   5:     <local:ContactGroupEditor contactGroup="{tree.selectedItem as ContactGroup}" enabled="{tree.selectedItem is ContactGroup}" />

   6:     <mx:HRule width="100%" />

   7:     <local:ContactEditor contact="{tree.selectedItem as Contact}" enabled="{tree.selectedItem is Contact}" />

   8: ...

An that’s it!  A simple but fully functioning, database aware Flex application.

And now, the thrilling conclusion.

Transforming ArrayCollection data using a map function to keep your view seperated in an MVC app

Last night I was working on a project which involved me having to display large amounts of data in a Flex list.  However, this data needed to be transformed before being displayed.  Let me explain with a simplified example.

The problem

Suppose we need to display a huge list of Result objects, where a Result object contains a score property (lets say it is a Number).  If we just need to display the number then we provide the ArrayCollection of Results as the dataProvider to the list and set the dataField to ‘score’ and everything is nice and easy.  Now suppose that we need to do some kind of calculation on score – for example, we need to divide it by 2 before displaying it.  Again, this is no problem; we use the labelFunction in the list and add a callback function in the MXML file that divides the score by two before returning it:

   1: private function labelFunction(item:Object):String {

   2:     var result:Result = item as Result;

   3:     return result.score / 2;

   4: }

This is all fine.

Now lets suppose that we need to transform the score in a more complicated way – imagine that we are writing some kind of multiuser application and we need to divide the score by the number of users currently logged on to the application before displaying it in the list.  At this point I should point out that I tend to use PureMVC so I will explain this using PureMVC terminology, but its equally applicable to all other MVC frameworks (Mate, Swiz, Robotlegs, etc).

Lets assume that the number of users logged on is in our Model tier; we then have a couple of options on how to go about this:

Inject the number of users into the view from our mediator and continue to use labelFunction.

       1: public var userCount:Number;

       2:

       3: private function labelFunction(item:Object):String {

       4:     var result:Result = item as Result;

       5:     return results.score / userCount;

       6: }

This is an ok solution – it doesn’t break MVC encapsulation and it works fine.  However, the drawbacks are that we need to monitor when userCount changes and each time re-inject it into the view (and tell the list to update itself).  But the main problem with this is that it gives our view knowledge of something that it shouldn’t know about.

Extend Result to do the calculation itself

We can add a getter to Result that does the calculation which means we can get rid of labelFunction in the view and just set dataField to the getter (e.g. dataField=”transformedScore”).  But this just moves the problem elsewhere; now the Result object will need to know the number of users; we’d either have to inject it in via a static variable, or give the Result object knowledge of the MVC framework.  This solution is in fact much worse than our first effort.

Pre-build the ArrayCollection with transformed scores

In this method we iterate through the Results, doing our calculation on score each time and building up a new dataprovider.  This is fine from an MVC perspective; the view gets its data fully formed in the format it requires, and since the mediator is building the list there is no problem with it querying the model tier.  However, this is a bad solution for other reasons; say that you have 5000 Result objects; the mediator will need to iterate through the entire list before passing the new collection to the view, incurring a nasty performance hit.  Furthermore we will need to rebuild this list entirely if the score or the number of logged in users change.

The solution

Here, at last, is my solution to the problem.  Like all good solutions it is disarmingly simple 🙂

   1: package org.davekeen.collections {

   2:     import mx.collections.IList;

   3:     import mx.collections.ListCollectionView;

   4:

   5:     public class MappedListCollectionView extends ListCollectionView {

   6:

   7:         public var mapFunction:Function;

   8:

   9:         public function MappedListCollectionView(list:IList = null) {

  10:             super(list);

  11:         }

  12:

  13:         override public function getItemAt(index:int, prefetch:int = 0):Object {

  14:             if (mapFunction == null) {

  15:                 return super.getItemAt(index, prefetch);

  16:             } else {

  17:                 return mapFunction(super.getItemAt(index, prefetch));

  18:             }

  19:         }

  20:

  21:     }

  22:

  23: }

MappedListCollectionView extends the normal ListCollectionView (which is the superclass of ArrayCollection) adding a single property – mapFunction – which transforms an item of the array collection into another item.  So now we can do the following in the mediator:

   1: var mappedListCollectionView:MappedListCollectionView = new MappedListCollectionView(resultsArrayCollection);

   2:

   3: mappedListCollectionView.mapFunction = function(item:Object):String {

   4:     var userCount:Number = getUserCountFromModel();

   5:

   6:     var result:Result = item as Result;

   7:     return result.score / userCount;

   8: }

   9:

  10: myView.myList.dataProvider = mappedListCollectionView;

I think this is an excellent solution – it maintains encapsulation within the view, it follows Flex standards (in that its syntax is the same as filterFunction), it will work with Flex binding and COLLECTION_CHANGE events and best of all it is high performance as mapFunction is only called for the rows that are actually on the screen.

One thing to bear in mind when using this solution is that Flex databinding will only be triggered when elements of the array collection itself change, so if userCount changes you will need to manually dispatch a COLLECTION_CHANGE event on the array collection.

Hope you find this as useful as I do!

Using addItemAt and removeItemAt on a DataGrid dataprovider

According to the notes buried deep within the Flex documentation:

If you use the ICollectionView interface to sort or filter a collection, do not use the IList interface to manipulate the data, because the results are indeterminate.

This means that if you are using an ArrayCollection as the dataprovider of a sortable component (e.g. a DataGrid), sort the columns and then try and use addItemAt() or removeItemAt() elsewhere in your application you will be entering a world of pain; the wrong items will be added/deleted in the datagrid, rows might get duplicated in strange way, etc.

The reason for this is that sorting the datagrid is changing the ListCollectionView of the original ArrayCollection which affects subsequent index based operations.

Luckily there is a simple solution; give the component its own personal ListCollectionView:

   1: dataGrid.dataProvider = new ListCollectionView(arrayCollection);

This preserves IList integrity on the original collection which means that databinding should work as expected.

Watch and learn – Data binding in Flex

My apologies to those of you who are watching my blog for updates – I’ve been working flat out on a number of exciting projects over the last few months including a plugin-based XML editor written in Flex which will be released as open source software in the coming weeks.

In the meantime I wanted to point everyone’s attention to this brilliant lecture which explains Flex data binding at the compiler level and finally tells us what [Bindable] really does, why we need to use ArrayCollection instead of Array and all the other internals of Flex architecture which the official docs gloss over.

http://www.onflex.org/ted/2008/08/diving-in-data-binding-waters-with.php