Mobile ViewNavigatorApplication navigator access

derekbrigner's Avatar

derekbrigner

24 Oct, 2011 02:39 PM

I recently started working on a mobile application and wanted access to the navigator. I have seen one discussion on here that suggests mapping the navigator by using FlexGlobals.topLevelApplication.navigator. What I am wondering is whether it would better to reference the navigator through the contextView by casting the contextView as a ViewNavigatorApplication. Please view the attached code for an example.

  1. Support Staff 1 Posted by Ondina D.F. on 26 Oct, 2011 05:04 PM

    Ondina D.F.'s Avatar

    Hi Derek,
    There is a similar question in this thread:
    http://knowledge.robotlegs.org/discussions/problems/405-robotlegs-a... with links to 3 examples.

    Personally, I would use the root display object (Application or ViewNavigatorApplication or TabbedViewNavigatorApplication ) as a contextView for a mobile app.

    I would do it like this:

    <s:ViewNavigatorApplication
    xmlns:context="yourPathToTheApplicationContext.*">
    <fx:Declarations>
    <context:ApplicationContext contextView="{this}"/>
    </fx:Declarations>

    or like this:
    <s:ViewNavigatorApplication
    preinitialize="application_preinitializeHandler(event)">

    import yourPathToTheApplicationContext.ApplicationContext;

    protected var context:ApplicationContext;

    protected function application_preinitializeHandler(event:FlexEvent):void
    {

    context=new ApplicationContext(this);

    }

    ApplicationContext

    
    public class ApplicationContext extends Context
    {
    
    public function ApplicationContext(contextView:DisplayObjectContainer=null, autoStartup:Boolean=true)
    {
       super(contextView, autoStartup);
    }
    override public function startup():void
    {
    super.startup();
    }
    ...
    }
    

    Does this answer your question?
    Ondina

  2. 2 Posted by derekbrigner on 27 Oct, 2011 08:41 PM

    derekbrigner's Avatar

    Hi Ondina,

    Thank you for your response. It does not answer my question. The question I have is of using the navigator from the top level application(ViewNavigatorApplication). In my example I map the following value:

    (application context) injector.mapValue(ViewNavigator,(contextView as ViewNavigatorApplication).navigator);

    This gives me a reference to the top level applications navigator property so that I can change the view stack from a Mediator or Command if I wanted to with the following injection:

    [Inject] public var navigator:ViewNavigator;

    I got this idea from the following discussion:

    http://knowledge.robotlegs.org/discussions/problems/263-mobileappli...

    In which Max mentions injecting the navigator by mapping the value:

    (application context) injector.mapValue(ViewNavigator, FlexGlobals.topLevelApplication.navigator);

    What I am wondering is if it is better to use FlexGlobals.topLevelApplication to reference the navigator, or if it would be better to cast the contextView property in the application context as the ViewNavigatorApplication to get the reference to the navigator. It may just be a matter of personal preference, but I have not seen any examples of ViewNavigatorApplication mobile applications using robotlegs.

    Thanks you again for your response,

    Derek

  3. Support Staff 3 Posted by Ondina D.F. on 01 Nov, 2011 01:34 PM

    Ondina D.F.'s Avatar

    Hi Derek,

    Sorry for the late response. Your response to my response to your question was hidden until yesterday, due to some tender issues.

    I’ll take another look at your question, and I’ll try to give you a more thorough answer or an example, later today or tomorrow.

    One thing I can say right now:
    Personally I wouldn’t make use of the FlexGlobals.topLevelApplication.

    I would map a view stack, of any kind, to a Mediator. I would let the ViewStackView handle the navigation and if switching between the views in the stack should trigger other actions, I would dispatch an event from the view, and the ViewStackMediator would redispatch it to whoever would want to listen.

    Or if I’d like to change the ViewStack’s index due to something happening elsewhere in the application, say a Model or Service, I’d let the ViewStackMediator listen for an event dispatched by the Model/Service. I would then call a public function on the ViewStackView , that would change the stack’s index to the one received through the event’s payload.

    Model: dispatch (new StackEvent(StackEvent.INDEX_CHANGED, viewStackIndex));

    Mediator: eventMap.mapListener(eventDispatcher, StackEvent.INDEX_CHANGED, onIndexChanged);

    protected function onIndexChanged(event: StackEvent):void
    {

    view.changeStackIndex(event.payload);

    }

    View:
    public function changeStackIndex(viewStackIndex:int):void
    {

    someViewStack.selectedIndex =viewStackIndex;

    }

    Now, that works for a common Flex ViewStack. I’ll have to see if it works like this with a ViewNavigatorApplication/ TabbedViewNavigatorApplication and ViewNavigator and if it answers your question:

    “This gives me a reference to the top level applications navigator property so that I can change the view stack from a Mediator or Command if I wanted to with the following injection:”

    Till then,
    Ondina

  4. Support Staff 4 Posted by Ondina D.F. on 02 Nov, 2011 11:31 AM

    Ondina D.F.'s Avatar

    Hi Derek,

    I’ll attach an example of a TabbedViewNavigatorApplication and here is a link to the repository on github, and a fxp file:
    https://github.com/Ondina/robotlegs-incremental/tree/master/robotle...
    https://github.com/Ondina/robotlegs-incremental/blob/master/robotle...

    In here, I’ll paste just the code of interest for our discussion.
    The ContextView is a TabbedViewNavigatorApplication container with 2 Views: SomeView and AnotherView. On creationComplete I add an event listener for the tabbedNavigator’s IndexChangeEvent. When the tabbedNavigator.selectedIndex changes, the view dispatches the SomeViewStackEvent.STACK_INDEX_CHANGED, with the selectedIndex as a payload
    ApplicationMediator, which is the Mediator for ContextView listens for this event, and, if need be, it can re-dispatch it.
    1. That’s the way from ContextView -> ApplicationMediator ->rest of the Application
    2. The other way around is from somewhere in the Application-> ApplicationMediator-> ContextView.
    In my example SomeView contains a list. If the user selects an item, SomeView dispatches
    SomeViewStackEvent.STACK_INDEX_CHANGED, with the selectedIndex as a payload,
    SomeMediator listens for that event and re-dispatches it.
    ApplicationMediator listens for the event dispatched by SomeMediator and does this:
    view.changeStackIndex(event.viewStackIndex);
    In the changeStackIndex method of the ContextView:
    tabbedNavigator.selectedIndex= viewStackIndex;
    That’s all:)
    Now, since I’m a flex-mobile-applications newbie, I can’t say how it works with other scenarios, where you want to push/pop a View. I’d have to try it, but I’m pretty sure that the logic of using the mediator of one of the navigator-containers as a bridge between the application and the view is applicable as well. I wouldn’t manipulate views in commands (exception to the rule would be a popup).

    If you have a simple example with your specific scenario, we could take it from there.
    I haven’t had the time to take a proper look at Mike’s example (see the other thread I linked to), so I can’t say if it’s what you need, but at least it could inspire you

    Here are the code snippets:
    ContextView.mxml is the root display object. It is a TabbedViewNavigatorApplication:

    
    TabbedViewNavigatorApplication
    ViewNavigator id="someView"
                         label="SomeView"
    firstView="com.robotlegs.demos.robotlegsincremental.views.components.SomeView"
    ViewNavigator id="anotherView"
                         label="AnotherView"
    firstView="com.robotlegs.demos.robotlegsincremental.views.components.AnotherView"
    TabbedViewNavigatorApplication>
    

    The script in ContextView:

    
    import com.robotlegs.demos.robotlegsincremental.ApplicationContext;
    import com.robotlegs.demos.robotlegsincremental.controllers.events.SomeViewStackEvent;
    import mx.events.FlexEvent;
    import spark.events.IndexChangeEvent;
    
    protected var context:ApplicationContext;
    
    protected function application_preinitializeHandler(event:FlexEvent):void
    {
    context=new ApplicationContext(this);
    }
    protected function application_creationCompleteHandler(event:FlexEvent):void
    {
    tabbedNavigator.addEventListener(IndexChangeEvent.CHANGE, onStackIndexChange);
    }           
    public function changeStackIndex(viewStackIndex:int):void
    {
    if (viewStackIndex != -1 && viewStackIndex < tabbedNavigator.length)
    tabbedNavigator.selectedIndex=viewStackIndex;
    }
                
    protected function onStackIndexChange(event:IndexChangeEvent):void
    {
        dispatchEvent(new SomeViewStackEvent(SomeViewStackEvent.STACK_INDEX_CHANGED, tabbedNavigator.selectedIndex));
    }
    

    SomeView.mxml

    View
    List id="someViewList"
                x="10"
                y="18"
                width="228"
                labelField="somename"
                change="someViewList_changeHandler(event)"
    View
    

    The code in SomeView:

    
    import com.robotlegs.demos.robotlegsincremental.controllers.events.SomeViewStackEvent;
    import mx.collections.ArrayCollection;
    import spark.events.IndexChangeEvent;
    
    public function setListDataProvider(dataProvider:ArrayCollection):void
    {
       someViewList.dataProvider=dataProvider;
    }
    
    protected function someViewList_changeHandler(event:IndexChangeEvent):void
    {
    dispatchEvent(new SomeViewStackEvent(SomeViewStackEvent.STACK_INDEX_CHANGED, event.currentTarget.selectedItem.someid));
    }
    

    ApplicationMediator.as

    
    [Inject]
    public var view:ContextView;
    
    override public function onRegister():void
    {
    eventMap.mapListener(eventDispatcher, SomeViewStackEvent.STACK_INDEX_CHANGED, onStackIndexChanged);
    eventMap.mapListener(view, SomeViewStackEvent.STACK_INDEX_CHANGED, onStackIndexChangedFromStack);
    }
    
    protected function onStackIndexChanged(event:SomeViewStackEvent):void
    {
    view.changeStackIndex(event.viewStackIndex);
    }
    
    protected function onStackIndexChangedFromStack(event:SomeViewStackEvent):void
    {
    //can be redispatched to other actors
    trace("ApplicationMediator.onIndexChangedFromView(event)" + event.viewStackIndex);
    }
    

    Somemediator.as

    
    [Inject]
    public var view:SomeView;
    
    override public function onRegister():void
    {
    eventMap.mapListener(view, SomeViewStackEvent.STACK_INDEX_CHANGED, dispatch);
    }
    
    protected function onDataUpdated(event:SomeModelEvent):void
    {
    view.setListDataProvider(event.someUpdatedData);
    }
    

    Please, tell me what you think and why my approach wouldn’t solve your problem.
    I’m willing to try your exact use case, if you let me know about it:)
    Maybe other users will chime in as well.

    Ondina

  5. Support Staff 5 Posted by Ondina D.F. on 02 Nov, 2011 11:32 AM

    Ondina D.F.'s Avatar

    the attachment:
    robotlegs-mobile-tabbed.zip

  6. Support Staff 6 Posted by Ondina D.F. on 03 Nov, 2011 09:26 AM

    Ondina D.F.'s Avatar

    Hi Derek,
    I’m looking now at Mike’s example, and I think it answers your question.
    In his Context he does:
    injector.mapValue(ViewNavigator, (contextView as ViewNavigatorApplication).navigator)
    and
    injector.mapSingleton(NavigatorManager);

    Then he injects the ViewNavigator in the NavigatorManager
    [Inject] public var navigator:ViewNavigator;

    public function gotoView(viewClass:Class, data:Object = null):void
    {

        navigator.pushView(viewClass, data);
    

    }

    public function goHome():void
    {

        navigator.popToFirstView();
    

    }

    It’s a cool solution.
    My preference would be to let the View handle the navigation(push, pop), though.
    I would use a command to dispatch an event to the mediator with the viewClass as a payload. Not sure how right now, but I’m curious how it works, so I will try it.

    HTH

    Ondina

  7. Support Staff 7 Posted by Ondina D.F. on 03 Nov, 2011 02:38 PM

    Ondina D.F.'s Avatar

    Hi Derek,

    Here is what I did :

    1. SomeView (https://gist.github.com/1336453) dispatches an event when the List’s index changes.
    2. SomeMediator (https://gist.github.com/1336460) re-dispatches it
    3. SomeViewListEvent.LIST_INDEX_CHANGED triggers NavigationCommand (https://gist.github.com/1336471)
    4. NavigationCommand reads the payload, which is an id coming from the SomeView’s List,
    5. and the injected NavigationModel (https://gist.github.com/1336473) provides it with the viewClass corresponding to this id stored in stackViews:Array
    6. The NavigationCommand(https://gist.github.com/1336471) dispatches the
    7. NavigationEvent.ACTIVE_VIEW_CHANGED (https://gist.github.com/1336468) with the viewClass as a payload.
    8. ApplicationMediator (https://gist.github.com/1336448) listens for this event and in
    9. the ContextView (https://gist.github.com/1336441) the ViewNavigator goes to the specified view: navigator.pushView(viewClass);

    The entire code is here:
    https://github.com/Ondina/robotlegs-incremental/tree/master/robotle...
    and the fxp here:
    https://github.com/Ondina/robotlegs-incremental/blob/master/robotle...

    If Mike would chime in too, we could discuss the alternative solutions.
    Anyway, my point was, and still is, that you don’t need to access the ViewNavigator in other parts of the application. Its Mediator is the one who should communicate with the rest of the app.

    My question to you and Mike: are there use cases, where you really need to have a reference to the ViewNavigator in a Model or a Command? Maybe I don’t know enough about the intricacies of the flex-mobile development yet, so I’d like to hear what you think.

    Thanks,
    Ondina

  8. Ondina D.F. closed this discussion on 05 Jan, 2012 09:05 AM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac