Imperative to any application is a navigation system. Users should be able to easily move around in an application and locate the functionality they need. Flex applications can implement navigation in one of two ways: using states, as you learned in the earlier lessons, and by using navigator containers.
Some navigation will be completely at the user’s discretion, such as clicking a button to move to the home page or the checkout process. Other navigation can be tightly controlled by the developer—for example, a checkout process in which users cannot proceed to the next screen until certain conditions are met on an existing screen. In this lesson, you will implement both types of navigation.
Navigation enables users to move through your application and (just as important) enables you to control user movement through the application. You will enable both user-controlled movement and application-controlled movement in this lesson when implementing a checkout process for the e-commerce application. During this process, you need to control which screens users see and when they see them.
A navigator container is a container that implements one specific rule on positioning its children: Only one child can ever be seen at the same time. This allows for a multipage view within the application. To help visualize how this works, picture a stack of papers on your desk. At any time, only one page in that stack can be on top, and therefore visible. To do this, the ViewStack toggles the visibility of its children, always ensuring that one and only one child can be seen at any time.
The Navigation components are still implemented using the mx architecture, meaning they are the same components that existed in Flex 3. To enable developers to use both Spark and MX components as children of a ViewStack, the Flex SDK added an INavigatorContent interface. Any class that implements INavigatorContent can be used as a direct child of a ViewStack. In the Spark component set, there is a new container named NavigatorContent that implements the INavigatorContent interface and subclasses the Spark Group class. You can use NavigatorContent instead of Group for adding Spark elements as children of the ViewStack.
Although the ViewStack is the key element to implementing navigation in Flex, it does not intrinsically have a way to switch which child is visible; that must be done using another tool. You can use built-in tools to control the ViewStack or build your own.
Although ViewStack does not have any visible way for a user to navigate between the views, the Accordion and TabNavigator implement the same functionality as a ViewStack, but also provide user interface controls to allow the user to navigate between the views.
To control which child is shown in a navigator container, you can interact with the container’s selectedChild
or selectedIndex
property.
You use the selectedIndex
property to choose which child of the ViewStack should be displayed.
Use the selectedChild
property if you would rather indicate which child of the ViewStack should be displayed by referencing the name (id
) of the child rather than the numeric index. The selectedChild
property will display the appropriate container in the ViewStack based on the instance name provided in the id
property. The following example shows how to use plain Button components to control which child of the ViewStack is displayed using both selectedChild
and selectedIndex
:
When run, this code creates the result shown in the following figure, where a ViewStack is used with buttons.
With this brief overview of the navigator containers, you are now ready to implement navigation in your applications.
In this exercise, you will import three views that will comprise the checkout process, and add a view stack to allow the user to navigate between them.
Alternatively, if you didn’t complete the previous lessons or your code is not functioning properly, you can import the FlexGrocer.fxp project from the Lesson14/start folder. Please refer to Appendix A for complete instructions on importing a project should you ever skip a lesson or if you ever have a code issue you cannot resolve.
A few classes are already provided for you. You will be copying these classes into your application.
Run the Checkout application. Notice there are three pages of forms that you can navigate between by clicking the Proceed button.
The merged project after the paste should look like this:
If your views package doesn’t have a checkout package inside it, you should undo and try again.
Some new constructs have been introduced in the views.checkout classes that will be explained in more detail in Lesson 15, “Using Formatters and Validators.”
id
of orderInfo
.<valueObjects:OrderInfo id="orderInfo"/>
If a namespace isn’t automatically created for the valueObjects package, add one to the root node like this:
xmlns:valueObjects="valueObjects.*"
mx
namespace) that has an id
of vs
, and a height
and width
of 100%
.<mx:ViewStack id="vs" width="100%" height="100%">
</mx:ViewStack>
orderInfo
property for the CustomerInfo class, and set its height
and width
to 100%
.<checkout:CustomerInfo orderInfo="{orderInfo}" width="100%" height="100%"/>
If a namespace isn’t automatically added to the root node for the checkout package, manually add one like this:
xmlns:checkout="views.checkout.*"
If you look at the source of CustomerInfo, you will see that its root node is NavigatorContent, so it is allowable as a child of the ViewStack. Additionally, you will find that it has a public bindable property called orderInfo
, which is bound to the controls.
orderInfo
property for the CreditCardInfo class, and set its height
and width
to 100%
.<checkout:CreditCardInfo orderInfo="{orderInfo}" width="100%" height="100%"/>
Much like the CustomerInfo class, CreditCardInfo has a root node of NavigatorContent, making it allowable as a child of the ViewStack. It also has a public bindable property called orderInfo
, which is bound to the controls.
orderInfo
property for the Review class, and set its height
and width
to 100%
.<checkout:Review orderInfo="{orderInfo}" width="100%" height="100%"/>
Much like the CustomerInfo and CreditCardInfo classes, Review also has a root node of NavigatorContent, making it allowable as a child of the ViewStack. It also has a public bindable property called orderInfo
, which is bound to the controls.
proceed
event from both the CustomerInfo and CreditCartInfo classes, which will call a soon-to-be-written function called handleProceed()
and pass the implicit event
object as an argument.The code in the ViewStack should read like this:
Next you will need to write the handleProceed()
method.
<fx:Script>
block between the end of the Declarations block and the start of the ViewStack. Within the Script block, add a private method called handleProceed()
, which accepts an argument named event
as an instance of the flash.events.Event class and a void
return type. The body of the function should increment the selectedIndex
of the ViewStack by 1.private function handleProceed( event:Event ):void {
vs.selectedIndex = vs.selectedIndex + 1;
}
This method allows users to navigate from the CustomerInfo to the CreditCardInfo to the Review views of the ViewStack. Whether they are in the CustomerInfo or the CreditCardInfo view, clicking the Continue button will bring them to the next screen.
handleEdit()
, which accepts an event
as an instance of the Event class and a void
return type. The body of this method should set the selectedIndex
of the ViewStack back to element 0
.private function handleEdit( event:Event ):void {
vs.selectedIndex = 0;
}
This method will be used if the user clicks the Edit Information button on the Review screen.
editInformation
event that will call your handleEdit()
method:<checkout:Review orderInfo="{orderInfo}"
width="100%" height="100%"
editInformation="handleEdit( event )"/>
completeOrder
event that will call a soon-to-be-written handleComplete()
method.<checkout:Review orderInfo="{orderInfo}"
width="100%" height="100%"
editInformation="handleEdit( event )"
completeOrder="handleComplete( event )"/>
handleComplete()
that accepts an event
as an argument and a void
return type. Inside this method, set the selectedIndex
of the ViewStack back to 0
, and dispatch a new OrderEvent with the name placeOrder
that uses the orderInfo
property as the order for the event. Finally, reset the orderInfo
property to be a new OrderInfo instance.private function handleComplete( event:Event ):void {
//reset the navigation for the next order
vs.selectedIndex = 0;
//Send the info up to the event chain
dispatchEvent( new OrderEvent( 'placeOrder', orderInfo ) );
//Clear out this order object for next time
orderInfo = new OrderInfo();
}
Make sure that you have an import
statement for the OrderEvent class from the events package.
placeOrder
of the type OrderEvent
.<fx:Metadata>
[Event(name="placeOrder", type="events.OrderEvent")]
</fx:Metadata>
shoppingCart
as an instance of the ShoppingCart class.import cart.ShoppingCart;
[Bindable]
public var shoppingCart:ShoppingCart;
As always, ensure that you import the ShoppingCart class.
shoppingCart
property into the Review class instance’s shoppingCart
property.<checkout:Review orderInfo="{orderInfo}"
width="100%" height="100%"
shoppingCart="{shoppingCart}"
editInformation="handleEdit( event )"
completeOrder="handleComplete( event )"/>
This will allow a shoppingCart
property to be passed from the main application into the CheckoutView, and then into the Review class itself.
shoppingCart
is declared and instantiated. Remove the instantiation of the new ShoppingCart. Save and close ShoppingView.mxml.[Bindable]
public var shoppingCart:ShoppingCart;
Since you need to share a single shoppingCart
between the shopping and checkout view, you will create the single instance in the main application and pass a reference to each.
All that remains now is to allow the rest of the application to effectively use this new CheckoutView you have created.
Now that you have a class to handle the checkout process, you need to integrate it into the rest of the application.
<views:ShoppingView id="bodyGroup"
x="0" y="0"
width="100%" height="100%"
groceryInventory="{productService.products}" />
<s:Label text="(c) 2009, FlexGrocer" right="10" bottom="10"/>
Having this label defined after the other views will ensure it will always be seen on top of anything else in the application.
x
and y
value of 0
, and a height
and width
of 100%
.<views:CheckOutView
x="0" y="0"
width="100%" height="100%"/>
As you save the application now, you should have your first chance to find any typos in the CheckoutView class you created. If the application compiles without error, continue. If not, check the code you created in the CheckOutView and fix any errors before proceeding.
states
block in the FlexGrocer application, which contains two states: one called shoppingView, and the other called checkoutView.<s:states>
<s:State name="shoppingView"/>
<s:State name="checkoutView"/>
</s:states>
<views:ShoppingView id="bodyGroup"
width="100%" height="100%"
groceryInventory="{productService.products}"
includeIn="shoppingView"/>
<views:CheckOutView
x="0" y="0"
width="100%" height="100%"
includeIn="checkoutView"/>
includeIn="shoppingView"
to the List instance.<s:List left="200" height="52"
includeIn="shoppingView"
dataProvider="{categoryService.categories}"
itemRenderer="components.NavigationItem"
change="list1_changeHandler(event)">
handleCheckoutClick()
, accepts a MouseEvent
as an argument, has a void
return type, and sets the currentState
to checkOutView
. The second, handleFlexGrocerClick()
, accepts a MouseEvent
as an argument, returns void
, and sets the currentState
to shoppingView
.private function handleCheckoutClick( event:MouseEvent ):void {
this.currentState = "checkoutView";
}
private function handleFlexGrocerClick( event:MouseEvent ):void {
this.currentState = "shoppingView";
}
click
handler on the btnCheckout button, which calls the handleCheckoutClick()
method and passes the event
object. Also add a click
handler to the Flex Grocer button that calls the handleFlexGrocerClick()
method and passes an event
object.id
of shoppingCart
.<cart:ShoppingCart id="shoppingCart"/>
As always, ensure you add the proper namespace to the root node.
shoppingCart
to the public shoppingCart
properties of both the ShoppingView and CheckOutView components.This will allow a single instance of the shoppingCart to be used by both the views.
<views:ShoppingView id="bodyGroup"
width="100%" height="100%"
groceryInventory="{productService.products}"
shoppingCart="{shoppingCart}"
includeIn="shoppingView" />
<views:CheckOutView
x="0" y="0"
width="100%" height="100%"
shoppingCart="{shoppingCart}"
includeIn="checkoutView"/>
handlePlaceOrder()
that accepts an instance of the OrderEvent class as an argument and returns void
. This method will use an Alert to display information about the order, and will then empty the cart by assigning it a new instance of the shoppingCart class and set the currentState
back to shoppingView
.The Alert class has a static method named show. When you call this method and pass it a string, Flex will show an Alert box with your message. If you had a server to submit the order to, the call to that server-side method would go here. You can learn about that in Volume 2 of this book. For now, use an Alert box to display information about the order. As always, ensure that the events.OrderEvent and mx.controls.Alert classes are properly imported.
placeOrder
event, which calls your newly written handlePlaceOrder()
method and passes the event
object to it.<views:CheckOutView x="0" y="0"
width="100%" height="100%"
shoppingCart="{shoppingCart}"
includeIn="checkoutView"
placeOrder="handlePlaceOrder( event )"/>
Save your files and run the application. You should now be able to add elements to the cart, click the checkout button, navigate through the checkout process, and complete the order. The application then resets the cart and returns you to the shopping experience.
In this lesson, you have:
• Used a ViewStack to allow users to navigate the checkout process (pages 354–357)
• Learned about the NavigatorContent class, which allows Spark containers in a ViewStack (pages 352–353)
• Controlled navigation with the ViewStack’s selectedIndex
and selectedChild
properties (pages 357–358)