Walks like a duck, talks like a duck

By Apache on Sunday 30 May 2010 23:20 - Comments (3)
Categories: Flex, Geekstuff, Work, Views: 12.364

Some of you may already be familiar with duck typing from other languages like groovy.

If not you can ready about it here: http://en.wikipedia.org/wiki/Duck_typing

Anyway, I was working on a presentation, and in my example I was selecting Product-objects. And I didn't want to immediatly create InvoiceItems because 1) I didn't have an invoice object yet 2) it would require a really complex structure where I'm selecting Products and then when it's selected and/or the amount goes above 0 convert it to a different type of object and have the item renderer deal with it ... again complex solutions.

Then I remembered an idea I had on friday for an extension of the datagrid control without having to invasively extend the object to support certain action you want your custom component to have, like checkbox selection, custom background color for a row, etc

What I came up with was using the ObjectProxy class to wrap a real domain object but next to it takes any property you set on it. So you can store whatever ui state data on the object that behaves exactly like the business object, also when iterating over all the object properties or serializing the object.

I know you can just declare a class dynamic in actionscript, but this isn't what you want on a real domain object, it has fixed properties and you usually want to persist it in the end.

Little example how it looks

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<s:List width="100%" height="100%" allowMultipleSelection="true">
        <s:dataProvider>
            <util:DynamicElementArrayList origSource="{AppModel.products}" id="dynList" />
        </s:dataProvider>
        <s:itemRenderer>
            <fx:Component>
                <s:ItemRenderer>
                    <s:HGroup width="100%" height="25" verticalAlign="middle" doubleClickEnabled="true" doubleClick="data.amount++">
                        <s:CheckBox selected="@{data.selected}" /> 
                        <s:Label text="{data.name}" /><mx:Spacer width="100%" /><s:Label text="{data.price}" />
                        <s:ComboBox selectedItem="@{data.amount}" requireSelection="true">
                            <s:ArrayCollection>
                                <fx:Array>
                                    <fx:Number>1</fx:Number>
                                    <fx:Number>2</fx:Number>
                                    <fx:Number>3</fx:Number>
                                    <fx:Number>4</fx:Number>
                                    <fx:Number>5</fx:Number>
                                </fx:Array>
                            </s:ArrayCollection>
                        </s:ComboBox>
                    </s:HGroup>
                </s:ItemRenderer>
            </fx:Component>
        </s:itemRenderer>
    </s:List>



The dynamicElementArrayList will just wrap each object inside a DynamicElement class.

And the core of the DynamicElement are these 2 simple overrides:

Flash ActionScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
flash_proxy override function getProperty(name:*):* {
            if (wrappedObject.hasOwnProperty(name))
            {
                return super.getProperty(name);
            } else {
                return dynamicProperties[name];
            }
        }
        
        flash_proxy override function setProperty(name:*, value:*):void {
            var oldValue: *;
            if (wrappedObject.hasOwnProperty(name))
            {
                oldValue = wrappedObject[name]; 
                super.setProperty(name, value);
            } else {
                oldValue = dynamicProperties[name];
                dynamicProperties[name] = value;
            }
            
            // binding
            this.dispatchEvent(new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE, false, false, null, this, oldValue, value, this));
        }



Similar things can be done in php using __get() and __set(), and probably in other languages as well.

In the end it allows me to have this screen backed by regular old domain objects, but I can also get/set the metadata I need for the ui, without having to create new classes every time.

http://tweakers.net/ext/f/0R7pI6hMfL8tuLPEAOxB2wqo/full.png

can be handled as, where obj.selected and obj.amount are not on the original object, and the ObjectUtil.toString does not show the new dynamic properties and also identifies the object class as the original domain class.

Flash ActionScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for each (var obj: Object in dynList)
                {
                    trace("----------------------------");
                    if (obj.selected)
                    {
                        trace(ObjectUtil.toString(obj));
                        trace("amount: " + obj.amount);
                    }
                    else
                    {
                        trace("not selected:");
                        trace(ObjectUtil.toString(obj));
                    }
                    
                }



http://codecaster.nl/got/rmb/star1.gifhttp://codecaster.nl/got/rmb/star2.gifhttp://codecaster.nl/got/rmb/star3.gifhttp://codecaster.nl/got/rmb/star4.gifhttp://codecaster.nl/got/rmb/star5.gifhttp://codecaster.nl/got/rmb/stats.gif