Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: [1] 2
Print
Author Topic: Flex Mobile ViewNavigator and PureMVC  (Read 55883 times)
Deus
Newbie
*
Posts: 2


View Profile Email
« on: November 23, 2010, 11:12:22 »

Hi all,

I'm quite new to PureMVC, having learnt other framework before, I'm quite excited to develop a mobile app using the new Flex Mobile (Flash Builder Burrito).

I understood that the View must not know or aware of the PureVMC apparatus going on and it should communicate to it's Mediator via Event. The only exception to this rule is the Root whereby it has to initiate the PureMVC apparatus by calling ApplicationFacade.getInstance().startup(this) and passing its reference.

In normal Flex app, I would do this to instantiate my View's mediator.

:
override public function onRegister():void
        {
            // Create and register Mediators
            // components that were instantiated by the mxml application
            facade.registerMediator( new SplashScreenMediator( app.splashScreen ) );
            facade.registerMediator( new MainScreenMediator( app.mainScreen ) );
        }

But with the new ViewNavigator in Flex Mobile, how do i go around this? and the navigation is handle by this object. And how do i get the View's reference into my Mediator?

:
http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/spark/components/ViewNavigator.html
Logged
Deus
Newbie
*
Posts: 2


View Profile Email
« Reply #1 on: November 28, 2010, 07:45:56 »

Pretty quite here. No takers?  ???
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #2 on: November 28, 2010, 05:44:34 »

Sadly, I haven't yet gotten to Hero yet, which I assume this is part of, it's still in preview.

So, please feed back into this thread what you find. Is ViewNavigator the top level object of a Hero mobile app? In that case, you've probably got to just have a mediator for it, but the children's instantiation might be deferred as is the case with the Slacker AS3 Demo.

-=Cliff>
Logged
mikebritton
Full Member
***
Posts: 42


View Profile Email
« Reply #3 on: January 07, 2011, 07:57:26 »

My approach was to start the facade with my first view, then register the main mxml file's Mediator.  I have a Command for this with Notifications that start up each view's Mediator if it doesn't exist.

Now, however, I end up losing my Mediators unexpectedly.  Research continues...
Logged
TonySykes
Newbie
*
Posts: 2


View Profile Email
« Reply #4 on: February 01, 2011, 04:36:16 »

Hi All,
It does not appear to be just a deferred instantiation problem - I had a look at Slacker but it does not help. With these mobile apps create your first screen via -
<s:MobileApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
    xmlns:s="library://ns.adobe.com/flex/spark"
    firstView="views.MobileTestDriveCFHome">
(which could be your splash screen for example) and then you jump to other screens via -
navigator.pushView(AddEditView). So the problem is getting the reference of the screen/view to pass to the mediator (via deferred instantiation) - facade.registerMediator(???????)

- Tony
« Last Edit: February 01, 2011, 04:46:17 by TonySykes » Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #5 on: February 03, 2011, 08:14:48 »

So in ...

:
navigator.pushView(AddEditView)
Is AddEditView a classname? Is pushView responsible for instantiation?

-=Cliff>
Logged
TonySykes
Newbie
*
Posts: 2


View Profile Email
« Reply #6 on: February 04, 2011, 12:36:51 »

Hi Cliff,

Is AddEditView a classname? - Yes. Here is the help for pushView -

spark.components.ViewNavigator.pushView(factory:Class, data:Object=null, transition:ViewTransition=null):void
Pushes a new view to the top of the navigation stack.
Parameters:
factory The class used to create the view
data The data object to pass to the view
transition The view transition to play

Is pushView responsible for instantiation? - Yes for instantiating the view.

Here is a simple app (thanks Arjan) showing a list of people and when you click on a person the details screen displays.

The main file has a firstView which calls views\ViewNavigationHome.mxml and the PeopeDetails view is then created via the click handle of the List peopleList

It is at this time we would want to do the delayed instantiation of a mediator for the PeopeDetails view. The question is not sure how to do this - I think I am missing something.


main.xml

:
<?xml version="1.0" encoding="utf-8"?>
<s:MobileApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
firstView="views.ViewNavigationHome">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>

</s:MobileApplication>

views\ViewNavigationHome.mxml

:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
title="Home" initialize="init()">


<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;

[Bindable]
private var people:ArrayCollection;

private function init():void
{
people = new ArrayCollection();
var somebody:Object = new Object();
// TODO
}

private function handleClick():void
{
navigator.pushView( PeopleDetails, peopleList.selectedItem );
}



]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>



<s:List id="peopleList" click="handleClick()" dataProvider="{people}"
labelField="name" width="100%" height="100%"/>

</s:View>

PeopleDetails.xml

:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
title="Person Details">

<fx:Script>
<![CDATA[
private function gotoHome():void
{
navigator.popToFirstView();
}
]]>
</fx:Script>
<s:layout>
<s:VerticalLayout paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5"/>
</s:layout>

<s:navigationContent>
<s:Button label="Home" click="gotoHome()"/>
</s:navigationContent>

<s:Form width="100%" height="100%">
<s:FormItem label="Name:" width="100%">
<s:Label text="{data.name}"/>
</s:FormItem>
</s:Form>

</s:View>

-Tony
Logged
shredding
Newbie
*
Posts: 6


View Profile Email
« Reply #7 on: June 12, 2011, 01:36:11 »

Hello all,

I'm reactivating the thread, 'cause I'm getting started with Flex Mobile too and am lacking a great idea of integrate it with pureMVC.

May I ask you to bring us up to date your research?

I'm playing around with a few ideas. Most of them are build this way:

I'm registering a navigatorMediator as MainMediator & it holds a reference to the Navigator and a <vector> with all views. When the user navigates, the navigatorMediator is called, registeres a dependent mediator and so on.

This is somewhat counterintuitive and I'm wondering if there's a better solution.

Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #8 on: June 12, 2011, 04:02:33 »

I'm registering a navigatorMediator as MainMediator & it holds a reference to the Navigator and a <vector> with all views. When the user navigates, the navigatorMediator is called, registers a dependent mediator and so on.

This is somewhat counterintuitive and I'm wondering if there's a better solution.

The NavigatorMediator should not need a vector of the child Views, only a reference to the ViewNavigator. It should listen for the ViewNavigatorEvent.VIEW_ADD event on its view component (the ViewNavigator). The ViewNavigatorEvent's view property will contain the View that was added.

Two possibilities at this point:
1) Have the ViewNavigatorMediator register the appropriate mediator for the newly added View,

OR

2) Send off a notification with the View in the body, and place the logic to register the appropriate mediator in the command that handles that notification.

Either way is perfectly valid.

#1 is fewer files, less complex, easier to maintain since fewer actors and steps are involved.

#2 is closer to a strict interpretation of the actors' intended roles: The Mediator merely mediates communication; takes an event and sends off a corresponding note. The Command handles logic, including organization of the view hierarchy.

-=Cliff>
« Last Edit: June 12, 2011, 04:11:25 by puremvc » Logged
mikebritton
Full Member
***
Posts: 42


View Profile Email
« Reply #9 on: June 12, 2011, 04:41:33 »

#2 is the way I chose to deal with it.  I wanted to centralize this with a Command and a Notification for all removal / instantiation.

It feels laborious to remove / instantiate new Mediators every time a view is pushed, so my next step is to encapsulate it somehow.  A plugin?  Dunno yet...

I stopped there, when I had a decent boilerplate to use for projects, then went and did the same thing for the Java port.
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #10 on: June 13, 2011, 07:40:39 »

It feels laborious to remove / instantiate new Mediators every time a view is pushed
If the View's creation is deferred until the time it's pushed into the ViewNavigator, then you don't have much choice but to wait until they exist before mediating them.

However, if you create all the views up front, before they're pushed into the ViewNavigator, then you'll probably incur a bit of perceived startup lag, but it might not be that bad if the Views aren't very complex.

If you took this latter approach, then you could create your views and mediate them in the traditional view prep phase of the startup process. Then when a specific view is needed, you could send a note that its mediator will hear and respond to by sending off a PUSH_VIEW note to the ViewNavigatorMediator. It in turn takes the View from the body of the note and pushes it on its view component, the ViewNavigator.

-=Cliff>
Logged
shredding
Newbie
*
Posts: 6


View Profile Email
« Reply #11 on: June 13, 2011, 12:48:00 »

Thanks for your answers.

I'll dive in & report :)
Logged
shredding
Newbie
*
Posts: 6


View Profile Email
« Reply #12 on: June 14, 2011, 02:03:47 »

Well, thanks. I got it up and running. Since I'm fairly new, would you mind take a look if this is ok?

Thank you all!

The Event is not ViewNavigatorEvent.VIEW_ADD, but ElementExistenceEvent.ELEMENT_ADD!

See: http://opensource.adobe.com/wiki/display/flexsdk/View+and+ViewNavigator

I'm booting the framework with the ViewNavigator like this:

:
<?xml version="1.0" encoding="utf-8"?>
<s:ViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
firstView="views.TemplateHomeView"
applicationDPI="240"
initialize="onInitalization(event)">
<fx:Script>
<![CDATA[
import com.digitaleavantgarde.ApplicationFacade;

import mx.events.FlexEvent;

private var facade:ApplicationFacade = ApplicationFacade.getInstance();


protected function onInitalization(event:FlexEvent):void
{

// Booting Framework
facade.startup(this.navigator);

}
]]>
</fx:Script>
</s:ViewNavigatorApplication>

The rest is pretty straighforward, I have an application mediator and can handle the events.

Proof of concept:
:
public function ApplicationMediator(viewNavigator:Object=null)
{
super( NAME, viewNavigator );

viewNavigator.addEventListener(ElementExistenceEvent.ELEMENT_ADD, function(evt:ElementExistenceEvent):void {

var activeView:View = evt.element as View;
trace(activeView.title); // traces the title
});
}
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #13 on: June 14, 2011, 09:53:55 »

@shredding Good work! Thanks for pointing out the proper event name and doc location 1. I was looking at an old version 2 of Adobe's documentation. I'm a little surprised that they just dropped VIEW_ADD instead of deprecating it, but I suppose it was in beta at the time and all bets are off on the final shape of things.

Anyway your bootstrapping looks ok. I'd change the ApplicationMediator to take a strict type on the constructor and not allow a null default. If you're immediately going to work with the object passed in, then you can't allow null.

Also I would set the listeners in onRegister. The argument for this as a best practice is that if the mediator receives an event from its view component before being registered, it can still send notes, but logically cannot participate in any conversation with other actors that might ensue, since it could not yet hear any return notes destined for it.

:
public function ViewNavigatorMediator(viewComponent:MyHeroApplication)
{
super( NAME, viewComponent );
}

private function get viewNavigator():MyHeroApplication
{
return viewComponent as MyHeroApplication
}

override public function onRegister():void
{
viewNavigator.addEventListener(ElementExistenceEvent.ELEMENT_ADD, onElementAdd)
}

private function onElementAdd(event:ElementExistenceEvent):void
{
var activeView:View = event.element as View;
trace(activeView.title); // traces the title

// now, let a command figure out which mediator to register...
sendNotification( MyAppConstants.MEDIATE_VIEW, activeView );
}

-=Cliff>

1 This is the right API page to look at currently: http://opensource.adobe.com/wiki/display/flexsdk/View+and+ViewNavigator

2 Adobe, does this still need to be up there? http://opensource.adobe.com/wiki/pages/viewpage.action?pageId=51839406#ViewandViewNavigator-APIDescription
« Last Edit: June 14, 2011, 09:57:03 by puremvc » Logged
shredding
Newbie
*
Posts: 6


View Profile Email
« Reply #14 on: June 14, 2011, 11:42:25 »

Thanks for pointing that out.

I've adjusted my ApplicationMediator. I had a discussion in the german flashforum. Someone gave me the interesting opinion, that the Mediator are upstreamed Controllers (while I was thinking there are downstreamed views ...).

Thus, most of the time I have to main Heroes in my Apps: The ApplicationMediator & the ApplicationProxy: The ApplicationProxy hosts all neccessary data & Proxies that are coupled to a concrete source (e.g. XMLProxy, DatabaseProxy ...) report to him & the VO. Thus, other proxies are more or less plugins to the Main ApplicationProxy.

On the other hand, the ApplicationMediator hosts a reference to the BaseView & handles some stuff like listening for Events that all Mediators care about.

Thus, I'm registering Mediators directly from the ApplicationMediator (and am using Commands rarely). Guess that's bad practice, huh?

Here's my final version.

E.g. the ApplicationFacade.LOG notification is logged always when something bad happens (Error Events). If I want to handle it via AlertBoxes or redirect it to the Javascript console or whatever, I can do it here.

The  ResourceLanguage Change (Basically it makes the IResourceManager available from none flex-based as3 files & manages access to language classes) should result in update the language in all Mediators - (setLang() is defined by the interface). Thus, an ApplicationMediator comes in handy for me.

Another Problem on the ViewNavigator: The element property of the ElementExistenceEvent returns a View - thus I cannot access the MyMediator.NAME const, as best practise would recommend without casting every single Mediator.

That's why I set the id == MyMediator.NAME on initalize & use it as synonym in the AppMediator ...

:
protected function onInitalization(event:FlexEvent):void
{
this.id = NAME;
                        }

:
package com.digitaleavantgarde.view
{

import com.digitaleavantgarde.ApplicationFacade;
import com.digitaleavantgarde.model.LocalStorageProxy;
import com.digitaleavantgarde.utils.lang.ResourceLanguage;

import flash.net.registerClassAlias;

import org.puremvc.as3.interfaces.IMediator;
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.mediator.Mediator;

import spark.components.View;
import spark.components.ViewNavigator;
import spark.events.ElementExistenceEvent;

import views.AnotherView;
import views.HomeView;

public class ApplicationMediator extends Mediator implements IMediator
{
public static const NAME:String = 'ApplicationMediator';

private var viewNavigator:ViewNavigator;

private var activeView:View;

public function ApplicationMediator(viewNavigator:ViewNavigator)
{
                        this.viewNavigator = viewNavigator;
super( NAME, viewNavigator );
}

override public function onRegister():void
{
viewNavigator.addEventListener(ElementExistenceEvent.ELEMENT_ADD, switchView);
}

override public function listNotificationInterests():Array {
return [
ApplicationFacade.LOG,
LocalStorageProxy.LANGUAGE
];
}

override public function handleNotification(notification:INotification):void {

var name:String = notification.getName();

switch (name)
{
case ApplicationFacade.LOG:
trace(notification.getBody());
break;

case LocalStorageProxy.LANGUAGE:

ResourceLanguage.getInstance().setLang(notification.getBody() as String);

if(activeView!=null) {
var viewToUpdate:ILanguageMediator = this.facade.retrieveMediator(activeView.id) as ILanguageMediator;
viewToUpdate.setLang();
}
break;
}
}

private function switchView(evt:ElementExistenceEvent):void {

if(activeView!=null) {
this.facade.removeMediator(activeView.id + 'Mediator');
}

activeView = evt.element as View;

switch(activeView.id) {
case(HomeView.NAME):
this.facade.registerMediator(new HomeViewMediator(activeView));
break;

case (AnotherView.NAME):
this.facade.registerMediator(new AnotherViewMediator(activeView));
break;
}
}

}
}

This works great for me, but I'm bit afraid of programming cross the purpose of the framework :)
« Last Edit: June 14, 2011, 11:45:23 by shredding » Logged
Pages: [1] 2
Print