Netflix API with PureMVC

PureMVC is another Actionscript heavy Flex framework that explicitly sends messages or ‘notifications’ around the framework to alert the application of changes, this is very different from Mate and Cairngorm that rely on data binding to update data within the application. What many people love about the PureMVC is the massive amount of delineation built into the framework – this helps modularize parts of your application. Along with the delineation people love the event/notification system, it’s like the Flex event system ON CRACK.

Let’s put it together.

The View
As with each of these framework examples I start with the same exact view.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?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/halo">
    <fx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.events.FlexEvent;
            import com.unitedmindset.utils.FormUtil;
            import spark.events.TextOperationEvent;
           
            protected function requestTokenButton_clickHandler(event:MouseEvent):void
            {
                var a:Array = [consumerKeyV,consumerSecretV];
                FormUtil.formValidation(a,actuallyRequestToken);
            }
           
            private function actuallyRequestToken():void
            {
            }

            protected function requestAccessTokenButton_clickHandler(event:MouseEvent):void
            {
                // TODO Auto-generated method stub
            }
           
            protected function textinput1_changeHandler(event:TextOperationEvent):void
            {
                var textInput:TextInput = event.target as TextInput;
                if(textInput.text.length==0){
                    //empty
                    return;
                }
                //run
            }

        ]]>
    </fx:Script>

    <s:layout>
        <s:HorizontalLayout paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20" gap="20"/>
    </s:layout>
    <fx:Declarations>
        <!--validations-->
        <mx:StringValidator id="consumerKeyV" source="{consumerKeyInput}" property="text" minLength="24"/>
        <mx:StringValidator id="consumerSecretV" source="{consumerSecretInput}" property="text" minLength="10"/>
    </fx:Declarations>
    <mx:Form>
        <mx:FormHeading label="Netflix Keys"/>
        <mx:FormItem label="Consumer Key" required="true">
            <s:TextInput id="consumerKeyInput" text=""/>
        </mx:FormItem>
        <mx:FormItem label="Consumer Secret" required="true">
            <s:TextInput id="consumerSecretInput" text=""/>
        </mx:FormItem>
        <mx:FormItem>
            <s:Button label="Request Token" id="requestTokenButton" click="requestTokenButton_clickHandler(event)"/>
        </mx:FormItem>
        <mx:FormHeading label="Netflix Access Token"/>
        <mx:FormItem>
            <s:Button label="Request Access Token" id="requestAccessTokenButton" click="requestAccessTokenButton_clickHandler(event)" enabled="false"/>
        </mx:FormItem>
    </mx:Form>
    <s:VGroup width="100%" height="100%">
        <s:HGroup width="100%">
            <mx:Label text="Search Catalog"/>
            <s:TextInput change="textinput1_changeHandler(event)"/>
        </s:HGroup>
        <s:List width="100%" height="100%" labelField="title"/>
    </s:VGroup>
</s:Application>

Which produces…

PureMVC Application

PureMVC Application

Time to start those engines… soon
One bit of annoyance that I have with PureMVC is the MASSIVE amount setup, basically we have to build the engine before we can start the car… a large complex engine. So let’s start building…

The Application Facade
The Application Facade is your starting point and main singleton object that is your connection to all of your models and mediators. As important as this sounds the Application Facade is actually a very simple starting point and it’s main job is to start up the application. My Application Facade is as follows:

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
27
28
29
30
31
32
package com.unitedmindset.controller.facade
{
    import com.unitedmindset.controller.macro.ApplicationStartupCommand;
   
    import org.puremvc.as3.patterns.facade.Facade;

    public class ApplicationFacade extends Facade
    {
        public function ApplicationFacade()
        {
            super();
        }
       
        public static function getInstance():ApplicationFacade
        {
            if(instance==null)
                instance = new ApplicationFacade();
            return instance as ApplicationFacade;
        }
       
        override protected function initializeController() : void
        {
            super.initializeController();
            registerCommand(ApplicationNotifications.STARTUP,ApplicationStartupCommand);
        }
       
        public function startup(app:NetflixPureMVC):void
        {
            sendNotification(ApplicationNotifications.STARTUP,app);
        }
    }
}

As you can see the Application Facade registers the startup command and waits for the startup function to be called. The real magic is what happens when this startup command is called, the entire application comes to life, the listeners are all registered, proxies are created, and the view is prepped for user interaction. How does this work exactly?

Application StartUp Diagram

Application StartUp Diagram


The diagram lays out the different parts that are created and registered on startup, the code for each part is featured below.

Application Startup Command

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.unitedmindset.controller.macro
{
    import com.unitedmindset.controller.simple.ModelPrepCommand;
    import com.unitedmindset.controller.simple.ViewPrepCommand;
   
    import org.puremvc.as3.patterns.command.MacroCommand;

    public class ApplicationStartupCommand extends MacroCommand
    {
        public function ApplicationStartupCommand()
        {
            super();
        }
       
        override protected function initializeMacroCommand() : void
        {
            //
            addSubCommand( ModelPrepCommand );
            addSubCommand( ViewPrepCommand );
        }
    }
}

Model Prep Command

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
package com.unitedmindset.controller.simple
{
    import com.unitedmindset.model.proxy.AccessProxy;
    import com.unitedmindset.model.proxy.AccessTokenProxy;
    import com.unitedmindset.model.proxy.AutocompleteProxy;
   
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.command.SimpleCommand;

    public class ModelPrepCommand extends SimpleCommand
    {
        public function ModelPrepCommand()
        {
            super();
        }
       
        override public function execute(notification:INotification) : void
        {
            //register proxys
            facade.registerProxy( new AutocompleteProxy() );
            facade.registerProxy( new AccessProxy() );
            facade.registerProxy( new AccessTokenProxy() );
        }
    }
}

View Prep Command

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.unitedmindset.controller.simple
{
    import com.unitedmindset.controller.mediator.ApplicationMediator;
   
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.command.SimpleCommand;

    public class ViewPrepCommand extends SimpleCommand
    {
        public function ViewPrepCommand()
        {
            super();
        }
       
        override public function execute(notification:INotification) : void
        {
            //
            facade.registerMediator( new ApplicationMediator( notification.getBody() ) );
        }
    }
}

Views and Mediators
What is interesting about PureMVC is how the view is disconnected from the machine itself and run via events sent to mediators. Mediators hold references to the views that they control, sends/receives notifications from the rest of the application, and finally listens for events from their connected view to send notifications and execute methods. This separation of view and controller is paramount to proper MVC development and keeps your views clean of logic.

When putting together your mediators it is easiest if you keep to a process for the layout of your mediator.

1. Create your Mediator Class

1. Create your Mediator Class

This will create:

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
package com.unitedmindset.controller.mediator
{
   
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;

    public class ApplicationMediator extends Mediator implements IMediator
    {
       
        public function ApplicationMediator(name:String, viewComponent:Object=null)
        {
            super(name, viewComponent);
        }
       
        override public function listNotificationInterests():Array
        {
            return null;
        }
       
        override public function handleNotification(notification:INotification):void
        {
        }
       
    }
}

Honestly, there are many other functions that are created, but these are the basic ones that you need to get going.

2. Next, name your mediator by adding a constant variable that we typically label “NAME”.

1
public static const NAME:String = "ApplicationMediator";

3. Add in local references to proxies that this mediator will need access to…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
//proxys
        private var autocompleteProxy:AutocompleteProxy;
        private var accessProxy:AccessProxy;
        private var accessTokenProxy:AccessTokenProxy;
       
        public function ApplicationMediator(viewComponent:Object=null)
        {
            super(NAME, viewComponent);
           
            autocompleteProxy = facade.retrieveProxy( AutocompleteProxy.NAME ) as AutocompleteProxy;
            accessProxy = facade.retrieveProxy( AccessProxy.NAME ) as AccessProxy;
            accessTokenProxy = facade.retrieveProxy( AccessTokenProxy.NAME ) as AccessTokenProxy;
...

Then add notifications that the mediator has interest in…

1
2
3
4
5
6
7
8
9
10
11
        override public function listNotificationInterests():Array
        {
            return [ApplicationNotifications.LOAD_AUTOCOMPLETE_LIST_SUCCESS,
                    ApplicationNotifications.LOAD_AUTOCOMPLETE_LIST_FAILED,
                    ApplicationNotifications.REQUEST_TOKEN,
                    ApplicationNotifications.REQUEST_ACCESS_TOKEN,
                    ApplicationNotifications.REQUEST_AUTOCOMPLETE_LIST,
                    ApplicationNotifications.LOAD_ACCESS_TOKEN_SUCCESS,
                    ApplicationNotifications.LOAD_ACCESS_TOKEN_FAILED,
                    ApplicationNotifications.LOAD_TOKEN_SUCCESS];
        }

Next add in some code to handle the notifications that the mediator is listening to…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...
        override public function handleNotification(notification:INotification):void
        {
            switch( notification.getName() ){
                case ApplicationNotifications.LOAD_AUTOCOMPLETE_LIST_SUCCESS:
                case ApplicationNotifications.LOAD_AUTOCOMPLETE_LIST_FAILED:
                break;
                case ApplicationNotifications.REQUEST_TOKEN:
                break;
                case ApplicationNotifications.REQUEST_ACCESS_TOKEN:
                break;
                case ApplicationNotifications.REQUEST_AUTOCOMPLETE_LIST:
                break;
                case ApplicationNotifications.LOAD_ACCESS_TOKEN_SUCCESS:
                break;
                case ApplicationNotifications.LOAD_TOKEN_SUCCESS:
                break;
            }
        }
...

The rest of the code that you need to add will be the listeners and methods needed to run the view. You can view my final Application Mediator. This mediator controls the main Application view file. You will see that I prefer to have references to my view’s components as getters in my mediator.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package com.unitedmindset.controller.mediator
{
    import com.unitedmindset.controller.facade.ApplicationNotifications;
    import com.unitedmindset.model.proxy.AccessProxy;
    import com.unitedmindset.model.proxy.AccessTokenProxy;
    import com.unitedmindset.model.proxy.AutocompleteProxy;
   
    import flash.events.Event;
   
    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;
   
    import spark.components.Button;
    import spark.components.List;
    import spark.components.TextInput;

    public class ApplicationMediator extends Mediator implements IMediator
    {
        public static const NAME:String = "ApplicationMediator";
       
        //proxys
        private var autocompleteProxy:AutocompleteProxy;
        private var accessProxy:AccessProxy;
        private var accessTokenProxy:AccessTokenProxy;
       
        public function ApplicationMediator(viewComponent:Object=null)
        {
            super(NAME, viewComponent);
           
            autocompleteProxy = facade.retrieveProxy( AutocompleteProxy.NAME ) as AutocompleteProxy;
            accessProxy = facade.retrieveProxy( AccessProxy.NAME ) as AccessProxy;
            accessTokenProxy = facade.retrieveProxy( AccessTokenProxy.NAME ) as AccessTokenProxy;
           
            app.addEventListener( NetflixPureMVC.REQUEST_TOKEN, requestToken_Handler );
            app.addEventListener( NetflixPureMVC.REQUEST_ACCESS_TOKEN, requestAccessToken_Handler );
            app.addEventListener( NetflixPureMVC.TEXT_INPUT_CHANGE, textInputChange_Handler );
        }
       
        override public function listNotificationInterests():Array
        {
            return [ApplicationNotifications.LOAD_AUTOCOMPLETE_LIST_SUCCESS,
                    ApplicationNotifications.LOAD_AUTOCOMPLETE_LIST_FAILED,
                    ApplicationNotifications.REQUEST_TOKEN,
                    ApplicationNotifications.REQUEST_ACCESS_TOKEN,
                    ApplicationNotifications.REQUEST_AUTOCOMPLETE_LIST,
                    ApplicationNotifications.LOAD_ACCESS_TOKEN_SUCCESS,
                    ApplicationNotifications.LOAD_ACCESS_TOKEN_FAILED,
                    ApplicationNotifications.LOAD_TOKEN_SUCCESS];
        }
       
        override public function handleNotification(notification:INotification):void
        {
            switch( notification.getName() ){
                case ApplicationNotifications.LOAD_AUTOCOMPLETE_LIST_SUCCESS:
                case ApplicationNotifications.LOAD_AUTOCOMPLETE_LIST_FAILED:
                    list.dataProvider = autocompleteProxy.autocompleteListDP;
                break;
                case ApplicationNotifications.REQUEST_TOKEN:
                    accessProxy.requestToken(consumerKeyInput.text, consumerSecretInput.text);
                break;
                case ApplicationNotifications.REQUEST_ACCESS_TOKEN:
                    accessTokenProxy.requestAccessToken();
                break;
                case ApplicationNotifications.REQUEST_AUTOCOMPLETE_LIST:
                    autocompleteProxy.loadAutocompleteList( textInput.text );
                break;
                case ApplicationNotifications.LOAD_ACCESS_TOKEN_SUCCESS:
                    textInput.enabled = true;
                break;
                case ApplicationNotifications.LOAD_TOKEN_SUCCESS:
                    requestAccessTokenButton.enabled = true;
                break;
            }
        }
       
        //----------------------
        //
        // View Component References
        //
        //----------------------
       
        protected function get app():NetflixPureMVC
        {
            return viewComponent as NetflixPureMVC;
        }
       
        protected function get textInput():TextInput
        {
            return app.autocompleteTextInput;
        }
       
        protected function get list():List
        {
            return app.list;
        }
       
        protected function get consumerKeyInput():TextInput
        {
            return app.consumerKeyInput;
        }
       
        protected function get consumerSecretInput():TextInput
        {
            return app.consumerSecretInput;
        }
       
        protected function get requestTokenButton():Button
        {
            return app.requestTokenButton;
        }
       
        protected function get requestAccessTokenButton():Button
        {
            return app.requestAccessTokenButton;
        }
       
        //----------------------
        //
        // Methods
        //
        //----------------------
        private function requestAccessToken_Handler(event:Event):void
        {
            sendNotification( ApplicationNotifications.REQUEST_ACCESS_TOKEN );
        }
        private function requestToken_Handler(event:Event):void
        {
            sendNotification( ApplicationNotifications.REQUEST_TOKEN );
        }
        private function textInputChange_Handler(event:Event):void
        {
            sendNotification( ApplicationNotifications.REQUEST_AUTOCOMPLETE_LIST );
        }
    }
}

Remember on of the main principals of application development: KISS. Mediators usually keep a 1 to 1 ratio with views, so when you are naming your mediators keep your view’s name in the mediator, this will keep it easy to see which mediators are attached to which view. Example: Application.mxml :: ApplicationMediator.as

Proxies
Proxies are your data centers of the application and act as stand ins for your data. This is really nice because you won’t have the issue of data not being available, causing wonderful runtime “null” pointer errors.

Again, let’s follow a process and put together our proxy.

1. Create the class

1. Create the class

This proxy we are setting up to handle a service call, that is why we included the IResponder interface (which provides us access to the result and fault methods). We also make sure to name our proxy with a NAME constant so we can access it later through the retrieveProxy method. Our current class looks as follows:

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
27
28
package com.unitedmindset.model.proxy
{
    import mx.rpc.IResponder;
   
    import org.puremvc.as3.interfaces.IProxy;
    import org.puremvc.as3.patterns.proxy.Proxy;

    public class AutocompleteProxy extends Proxy implements IProxy, IResponder
    {
        public static const NAME:String = "AutocompleteProxy";
       
        public function AutocompleteProxy( data:Object=null)
        {
            super(NAME, data);
        }
       
        public function result(data:Object):void
        {

        }
       
        public function fault(info:Object):void
        {

        }
       
    }
}

Our proxy will need a method to call the service and a getter to retrieve access to the proxy’s data. Our changed proxy looks as follows:

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
27
28
29
30
31
32
33
34
35
36
37
package com.unitedmindset.model.proxy
{  
    import mx.rpc.IResponder;
   
    import org.puremvc.as3.interfaces.IProxy;
    import org.puremvc.as3.patterns.proxy.Proxy;

    public class AutocompleteProxy extends Proxy implements IProxy, IResponder
    {
        public static const NAME:String = "AutocompleteProxy";
       
        public function AutocompleteProxy( data:Object=null)
        {
            super(NAME, data);
        }
       
        public function loadAutocompleteList(term:String):void
        {

        }
       
        public function result(data:Object):void
        {

        }
       
        public function fault(info:Object):void
        {

        }
       
        public function get autocompleteListDP():ArrayCollection
        {
            return null;
        }
    }
}

To complete the proxy we need to add in a delegate (more on delegates next) to actually make the service call in the load method, then add in the logic for our result/fault methods, and finally return the data in the data getter function. One thing to remember is that proxies include a data property to actually store the data that the proxy represents, so when you retrieve/set/access data, store that data in your proxy’s data property. Finally send out notifications to our engine that lets the rest of the application know that our service has received a result/fault.

The final proxy is here.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.unitedmindset.model.proxy
{
    import com.netflix.webapis.events.NetflixFaultEvent;
    import com.netflix.webapis.events.NetflixResultEvent;
    import com.unitedmindset.controller.facade.ApplicationNotifications;
    import com.unitedmindset.model.business.AutocompleteDelegate;
   
    import mx.collections.ArrayCollection;
    import mx.controls.Alert;
    import mx.rpc.IResponder;
   
    import org.puremvc.as3.interfaces.IProxy;
    import org.puremvc.as3.patterns.proxy.Proxy;

    public class AutocompleteProxy extends Proxy implements IProxy, IResponder
    {
        public static const NAME:String = "AutocompleteProxy";
       
        public function AutocompleteProxy( data:Object=null)
        {
            super(NAME, data);
        }
       
        public function loadAutocompleteList(term:String):void
        {
            var delegate:AutocompleteDelegate = new AutocompleteDelegate(this);
            delegate.getAutocompleteList(term);
        }
       
        public function result(data:Object):void
        {
            var result:NetflixResultEvent = data as NetflixResultEvent;
            this.data = new ArrayCollection(result.result as Array);
            sendNotification( ApplicationNotifications.LOAD_AUTOCOMPLETE_LIST_SUCCESS );
        }
       
        public function fault(info:Object):void
        {
            this.data = new ArrayCollection();
            var fault:NetflixFaultEvent = info as NetflixFaultEvent;
            Alert.show("Error occured while calling for autocomplete service.","System Alert");
            sendNotification( ApplicationNotifications.LOAD_AUTOCOMPLETE_LIST_FAILED );
        }
       
        public function get autocompleteListDP():ArrayCollection
        {
            return data as ArrayCollection;
        }
    }
}

Our Service Connection, the Delegate
Delegates are simple classes that hold a reference to the responder class that they will respond to (that is why our proxy implements the IResponder interface), a service reference, and methods that will actually connect with the service. Below is my AutocompleteDelegate. Usually I try to keep one delegate per service. Many services will include multiple methods, so my delegate will have one method per service method. Some people also like to have one delegate per service method, the choice is yours.

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
27
28
29
30
31
32
package com.unitedmindset.model.business
{
    import com.netflix.webapis.catalog.TitlesService;
    import com.netflix.webapis.catalog.params.CatalogParams;
    import com.netflix.webapis.events.NetflixFaultEvent;
    import com.netflix.webapis.events.NetflixResultEvent;
   
    import mx.rpc.IResponder;

    public class AutocompleteDelegate
    {
        private var _responder:IResponder;
        private var _service:TitlesService;
       
        public function AutocompleteDelegate(responder:IResponder)
        {
            _service = new TitlesService();
            _responder = responder;
        }
       
        public function getAutocompleteList(term:String):void
        {
            _service.addEventListener(NetflixResultEvent.RESULT,_responder.result);
            _service.addEventListener(NetflixFaultEvent.FAULT,_responder.fault);
           
            var params:CatalogParams = new CatalogParams();
            params.term = term;
           
            _service.autoCompleteService(params);
        }
    }
}

Back to the view
We have now outlined all the parts to the PureMVC framework setup, now we just need to see how the view connects to your new beautiful machine.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<?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/halo"
    creationComplete="application1_creationCompleteHandler(event)" viewSourceURL="srcview/index.html">
    <fx:Script>
        <![CDATA[
            import com.unitedmindset.controller.facade.ApplicationNotifications;
            import com.unitedmindset.controller.facade.ApplicationFacade;
            import mx.events.FlexEvent;
            import com.unitedmindset.utils.FormUtil;
            import spark.events.TextOperationEvent;
           
            public static const TEXT_INPUT_CHANGE:String = "textInputChange";
            public static const REQUEST_TOKEN:String = "requestToken";
            public static const REQUEST_ACCESS_TOKEN:String = "requestAccessToken";
           
            protected function application1_creationCompleteHandler(event:FlexEvent):void
            {
                var facade:ApplicationFacade = ApplicationFacade.getInstance();
                facade.startup(this);
            }
           
            protected function requestTokenButton_clickHandler(event:MouseEvent):void
            {
                var a:Array = [consumerKeyV,consumerSecretV];
                FormUtil.formValidation(a,actuallyRequestToken);
            }
           
            private function actuallyRequestToken():void
            {
                dispatchEvent( new Event( REQUEST_TOKEN ) );
            }

            protected function requestAccessTokenButton_clickHandler(event:MouseEvent):void
            {
                dispatchEvent( new Event( REQUEST_ACCESS_TOKEN ) );
            }


            protected function textinput1_changeHandler(event:TextOperationEvent):void
            {
                var textInput:TextInput = event.target as TextInput;
                if(textInput.text.length==0){
                    //empty
                    return;
                }
                dispatchEvent( new Event( TEXT_INPUT_CHANGE ) );
            }


        ]]>
    </fx:Script>

    <s:layout>
        <s:HorizontalLayout paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20" gap="20"/>
    </s:layout>
    <fx:Declarations>
        <!--validations-->
        <mx:StringValidator id="consumerKeyV" source="{consumerKeyInput}" property="text" minLength="24"/>
        <mx:StringValidator id="consumerSecretV" source="{consumerSecretInput}" property="text" minLength="10"/>
    </fx:Declarations>
    <mx:Form>
        <mx:FormHeading label="Netflix Keys"/>
        <mx:FormItem label="Consumer Key" required="true">
            <s:TextInput id="consumerKeyInput" text=""/>
        </mx:FormItem>
        <mx:FormItem label="Consumer Secret" required="true">
            <s:TextInput id="consumerSecretInput" text=""/>
        </mx:FormItem>
        <mx:FormItem>
            <s:Button label="Request Token" id="requestTokenButton" click="requestTokenButton_clickHandler(event)"/>
        </mx:FormItem>
        <mx:FormHeading label="Netflix Access Token"/>
        <mx:FormItem>
            <s:Button label="Request Access Token" id="requestAccessTokenButton" enabled="false" click="requestAccessTokenButton_clickHandler(event)"/>
        </mx:FormItem>
    </mx:Form>
    <s:VGroup width="100%" height="100%">
        <s:HGroup width="100%">
            <mx:Label text="Search Catalog"/>
            <s:TextInput change="textinput1_changeHandler(event)" id="autocompleteTextInput" enabled="false"/>
        </s:HGroup>
        <s:List width="100%" height="100%" labelField="title" id="list"/>
    </s:VGroup>
</s:Application>

Start your engines!
Now when your application starts up the framework is waiting for one command to get going:

1
facade.startup(this);

With this startup method called your engine is running and you are ready to go!

Final Application
This is the final PureMVC Example and the source.

PureMVC Application

PureMVC Application

  • Share/Bookmark

Comments (3)

Bob TribitAugust 21st, 2009 at 10:38 am

Nice! Thank you. Your take on PureMVC has been valuable.

ChrisAugust 25th, 2009 at 10:35 am

Excellent, so useful, thanks for sharing..

Mariush T.September 26th, 2009 at 9:36 am

Great example, Cheers!

Leave a comment

Your comment