February 08, 2007

Example of Designing Loosely Coupled Flex 2 Components --- pass data from main to child component

There is a good article regarding Designing Loosely Coupled Flex 2 Components. This is a good place to learn about different ways to design your components in Flex 2. On the third page of this article, it demonstrated how to Create Custom Event ActionScript Classes for sending complex data with an example. This example shows how to send data to the parent page by dispatch an event from the child component. What if you want to dispatch the event from the parent page, and pass the data into the child? Here is a simple example to show how we can accomplish that.

1. create a customer event ActionScript class:

In order to pass extra data via event, a customer event class is needed. We do this by extends existing Flex event classes and inherit all the events of the base class.  Here we create myEvent.as to pass selectedAlbum with the event.

myEvent.as:

package
{
     import flash.events.Event;
     import mx.core.Application;

    public class myEvent extends Event
   {
            public var myAlbum:String;
            public function myEvent(selectedAlbum:String,type:String) {
                     super(type);
                     this.myAlbum = selectedAlbum;
                     Application.application.lb.text=myAlbum;
           }
           public override function clone():Event{
                    return new myEvent(myAlbum,type);
           }
  }
}

2.  Create the main application:

The main application has a datagrid. The goal is to be able to display data in a new state based on selected row in the datagrid. The follwoing two handlers are the most improtant:

1).datagrid's itemClick event handler itemClickEvent():  this will get the selected row and value of selectedAlbum.

2).eventHandler(): this will display the new state, dispatch event myEvent (must make sure the event is dispatched after the child is created), and pass selectedAlbum to the child component. We reference the child component as <v:looselyCoupledChild />.

Note, eventObj = new myEvent(selectedAlbum,"passID");

Here, passID is the event name we will define in the Metadata of the child component:

<mx:Metadata>
[Event(name="passID", type="myEvent")]
</mx:Metadata>

main flex page looselyCoupled.mxml:

<?xml version="1.0" encoding="utf-8"?>

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:v="*" layout="vertical">

<!--This example passes selectedAlbum to child.mxml by listening  to myEvent -->        

<mx:Script>

        <![CDATA[    

                      import flash.events.EventDispatcher;

                      import mx.events.ListEvent;

                      import mx.collections.ArrayCollection;

                      [Bindable]

                      public var selectedAlbum:String;

                      public var eventObj:myEvent;

                      [Bindable]

                      private var initDG:ArrayCollection = new ArrayCollection([

            {Artist:'Pavement', Album:'Slanted and Enchanted', 

                      Price:11.99, SalePrice: true },

            {Artist:'Pavement', Album:'Brighten the Corners',   

                      Price:11.99, SalePrice: false }

          ]);  

        private function itemClickEvent(event:ListEvent):void {

                                    selectedAlbum=event.currentTarget.selectedItem.Album;

                                    eventObj = new myEvent(selectedAlbum,"passID");

                                    this.dispatchEvent(eventObj);

                                    lb1.text=eventObj.myAlbum;

      }

                             private function eventHandler(stateView:String):void{

                             //the stateView must be created before the event dispatch.

                                    this.currentState = stateView;

                                    dispatchEvt();

                      }

                      private function dispatchEvt():void{

                                    eventObj = new myEvent(selectedAlbum,"passID");

                                    this.dispatchEvent(eventObj);                                

                      }                

        ]]>

</mx:Script>

<mx:Label id="lb"  text="show selectedAlbum -- from myEvent"/>

<mx:Label id="lb1"  text="show selectedAlbum -- from eventObj.myAlbum"/>

<mx:DataGrid  id="dg" dataProvider="{initDG}"  itemClick="itemClickEvent(event)">

</mx:DataGrid>

<mx:Button x="117" y="240" label="dispaly Album"  id="bt" click="eventHandler('child')" />

          <mx:states>

                        <mx:State name="child">

                                      <mx:AddChild position="lastChild">

                                                                <v:looselyCoupledChild />

                                      </mx:AddChild>

                        </mx:State>

          </mx:states>     

</mx:Application>

3. create the child component:

In the child component, we need to add event listener to listen to passID as following:   Application.application.addEventListener("passID", passIDHandler);

looselyCoupledChild.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" initialize="initHandler()" >
<mx:Metadata>
[Event(name="passID", type="myEvent")]
</mx:Metadata>
<mx:Script>
<![CDATA[

import mx.core.Application;
import myEvent;
private function initHandler():void{
Application.application.addEventListener("passID", passIDHandler);
}

private function passIDHandler(event:myEvent):void {
tinput.text = event.myAlbum;
}

]]>
</mx:Script>

<mx:Label text="Album you selected from event:" x="26" y="10"/>
<mx:TextInput x="107" y="8" id="tinput" />
<mx:Label x="291" y="10" text="Album you selected from selectedAlbum:"/>
<mx:TextInput x="372" y="8" text="{Application.application.selectedAlbum}"/>

</mx:Panel>

Note, Application.application.addEventListener("passID", passIDHandler);   This means we will always have a reference to the child. If you are concerned about memory issue, you should check and destroy the event when it is done. If you don't want to destroy the event, you can set weakRef to true in addEventListener.

To test the sample,

1). run looselyCoupled.mx, and select a row. You will see the Album name displayed on top of the datagrid.

2). click on "dispaly Album" Button, you will see the Album name has passed into the child component.

3). select another row in the datagrid, you will see the value in the child component is changed as well.

Posted by lin at 05:16 PM | Comments (245)

February 21, 2006

Print Job in flex 2 beta 1

I had several tips for working with print job in flex 1.5 ( See Tips for using PrintingJob in flex 1.5). Now that the flex 2 beta is available to the public, I am trying to convert the examples to run with flex 2. The migration does not take a lot of work. However, there are some glitches. I am trying to document as much as I could to make the conversion easier for others. Needless to say, we are working with beta 1. Therefore, any thing can be changed in the future release. This is not intent to show how print job will work in flex 2. It is just an exercise to prepare myself for the final migration.

Before we start to talk about conversion for each example, we need to start with the general migration steps. Please see ActionScript 3 Learning Tips. For our code, the following are most important:

Declare types for all variables, parameters and return values.
For example: for (var i=0;i<pages;i++) should be for (var i:int=0;i<pages;i++)

Declarations with no access specifier now default to package internal, not public.
function doPrint(){} should be: public function doPrint(){}

Classes must be imported, even if references to the class are fully qualified.
Do not forget to do import mx.print.FlexPrintJob;

Properties are no longer bindable by default.
You must declare them to be bindable by using the [Bindable] metadata tag.

Functions must declare return types.
If a function does not return any value, declare its return type as void. Note,
although the document all indicating it should be "Void", it must be "void" with
lower case.

The next important thing to note is that flex 2 uses FlexPrintJob instead of PrintJob class. Also, it is using addObject() instead of addPage() method.

The above mentioned items are general for all the print examples and should be modified accordingly in each file.

1. Migrating printDataGrid.mxml from flex 1.5:

In flex 1.5, we handle the multiple pages by calculating the page numbers we needed to print out all the rows in the datagrid. In flex 2, a method nextPage() in PrintDataGrid will do that for you. printDataGridsave.mxml shows how to use that in flex 2.

Note, you should call updateLayout(); before you do addObject() to update the DataGrid layout to reflect the results of this function.

2. Can not print out header/footer area.

Print out header/footer area become much easier in flex 2.0. You can create a view, and you can include your own header/footer to specify the contents of the start and the end of the output. There is a sample in the doc you can use as a template.

If you just want to print out multiple components on a page, as in example printextra.mxml, you will need to put all the components into one container as you did in flex 1.5. However, you no longer need to set the print area explicitly.
See printextra2.mxml for example.

3. When the print area is larger than the screen, some of the content does not print out.

This is the same as it is in flex 1.5. You will need to set "clipContent='false' " at the application tag (you may not have to set at the container level any more. You still need to set the application's width/height to be large enough for the area you want to print.

4. When printing any chart, the print out always has gray background.

This is the same as it is in flex 1.5. You can either set the backgroundColor of the container that contains the chart to a color you desire, or explicitly set the fill of the chart.
Note, because the Properties are no longer bindable by default, you must declare them to be bindable by using the [Bindable] metadata tag before you define the dataProvider variable expenses: Array. See example chartprint.mxml.

Posted by lin at 06:11 PM | Comments (150)

June 06, 2005

Tips for using PrintingJob in flex 1.5

1. The sample from "Flex 1.0: Sample code for printing from a DataGrid" doesn't work correctly.
That sample was written for flex 1.0. To use it in flex 1.5, Use the attached PrintDatagrid.mxml instead.

2. Can't print out header/footer area.
Let's say you have a DataGrid you want to print, but you also want to print out a label or a button as a header of the DataGrid. To do so, you need to do two things:
1). Put the label/button and the DataGrid into a container, like VBox or Panel; in Printjob.addPage(), you need to reference the id of that container, not the DataGrid.
2). you need to set the print area properly, for example:
p.addPage("dance_mc",{xMin:-100,xMax:400,yMin:-150,yMax:560});
you need to adjust the xMin and yMin properly to include the header area. See printextra.mxml. This sample prints a button that is outside of the Panel.

3. When the print area is larger than the screen, some of the content doesn't print out.
This is because application is clipping its children. To avoid this problem, You can set "clipContent='false' " on both the application and the box your want to print. Note, in order for this to work properly, you need to set the application's width/height to be large enough for the area you want to print.

Let's say you want to print a DataGrid that is 500X600, and you set
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" width="100%" height="100%" >
When the browser screen is larger than 500X600, then the print will work correctly. However, if the user resizes the screen to 400X500, and then do the print, then some portion will be clipped. To make sure the print always work correctly, you need to set the application's width/height to be always Larger than 500X600.

4. When printing any chart, the print out always has gray background.
Charts, by default, have a transparent background with alpha = 0. They do this so they can show the background of whatever is behind them, but still catch mouse events for datatips. However, since printing doesn’t support alpha, the background is showing through as grey.
There are two ways to workaround this problem:
1). set the backgrandColor of the container that contains the chart to a color you desire.
2). explicitly set the fill of the chart like this:

<mx:ColumnChart id="column" dataProvider="{expenses}" >
<mx:fill>
<mx:SolidColor color="#FFFFFF" />
</mx:fill>
.....
</mx:ColumnChart>

Note, this approach may cause problem described in #3 above. You need to set the width and height at the app level to be big enough for your chart.


Posted by lin at 03:29 AM | Comments (300)