Showing posts with label flex4. Show all posts
Showing posts with label flex4. Show all posts

Friday, October 1, 2010

ResourceManager is only as good as the components you use it with

The flex framework provides many conveniences out-of-the-box, one of them being the ResourceManager. It works wells and until recently I thought one could do no wrong by it.

Yet, as I recently found out, even the most fool-proof components are only as good as the developer using them. Being someone who started working directly with Flex 4, I have had it way too easy. And sometimes I miss out on the bigger picture as I lack the fundamentals that someone who predates the flex 4 framework, would naturally have.

Recently I was faced with a surprising scenario where the ResourceManager had all the translations in various languages for a given key, yet it would always only load one set of translations. After loading the initial translation for a given language, it would simply refuse to update the label, even as I flipped through and tried to change the locales one by one.

The resources for the resource manager were being downloaded from the server side and loaded on the fly into the ResourceManager instance. Adobe has a very nice example of this on their live docs website. But the first translation for the first language to have appeared would stick around and not change!

Another colleague, who was not so naive as to simply take things at face value, pointed out to me that I was trying to use the ResourceManager too soon so perhaps the translation in my label was from a point in time when the rest of the translations had not made it over. But looking at IResourceManager we realized what a well-behaved component it is! As it makes an effort to fire-off change events in order to alert its users that perhaps now there are more exact/locale-specific translations for them to lookup. So what could possibly be wrong?

The AHA moment: my pre-flex-4 colleague noticed that the over-arching component containing the ResourceManager bindings was actually and since it did not implement, extend or mixin the event dispatcher functionality, it would NOT receive property change events! And as soon as I started using something like the problem disappeared.

It just goes to show you that frameworks cannot replace a competent and well-versed developer who excels in his/her area.

Wednesday, September 1, 2010

Flex4: How to build a Stop Watch Timer

Here's a working sample:

The following blog offered a series of very convincing arguments that led me to use flash.utils.getTimer() to build the Stopwatch demonstrated above.

The component constantly resets the time based on when it is made visible/invisible (click here twice to see it get reset +/-). If your components show a custom busy / loading skin rather than just a simple busy cursor, then you can leverage this component to also track how long your activities usually take visually.

Here's the source code:
  1. main.mxml
  2. StopWatch.as
  3. StopWatchSkin.mxml



Monday, May 17, 2010

Fluid Layout

Assumptions:
1) The browser represents the container.
2) The resizing of the container's width and height are primarily at the user's mercy and the components inside it must behave like water, which has no choice but to overflow if the beaker that it is being poured into is too small.

I've provided a sample that demonstrates what it would be like to have fluidity on the horizontal plane.

Click here to see the sample in action.

1) Try to resize your browser window and click on the collapsible and expandable right/left side buttons to see how the horizontal scroller behaves as the browser is resized.
2) If both the left and right side buttons stay expanded, then you will see the horizontal scrollbar appear after shrinking the browser window to be smaller than 500 pixels wide.
2) If both the left and right side buttons stay collapsed, then you will see the horizontal scrollbar appear after shrinking the browser window to be smaller than 300 pixels wide.

Here's the sample source.
<?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/mx">

    <fx:Script>
        <![CDATA[
            protected function expandCollapseHandler(event:Event):void
            {
                if (event.target.width>100) {
                    event.target.width=100;
                } else {
                    event.target.width=200;
                }
            }
        ]]>
    </fx:Script>

    <s:Scroller left="0" right="0" top="0" bottom="0">
        <s:Group width="100%" height="100%">
            <s:Group width="100%" height="100%">
                <s:Button id="buttonLeft"
                          minWidth="100"
                          width="200"
                          height="100%"
                          label="buttonLeft Height:{buttonLeft.height}, Width:{buttonLeft.width}"
                          click="expandCollapseHandler(event)"/>
                <s:Button id="buttonCenter"
                          left="{buttonLeft.width}"
                          right="{buttonRight.width}"
                          minWidth="100"
                          height="100%"
                          label="buttonCenter Height:{buttonCenter.height}, Width:{buttonCenter.width}" />
                <s:Button id="buttonRight"
                          right="0"
                          minWidth="100"
                          width="200"
                          height="100%"
                          label="buttonRight Height:{buttonRight.height}, Width:{buttonRight.width}"
                          click="expandCollapseHandler(event)"/>
            </s:Group>
        </s:Group>
    </s:Scroller>
</s:Application>
That's all folks.


Wednesday, May 12, 2010

Flex4: Nuances of a flexible UI (Part 1)

Given:
1) a s:Group with a width of 100
2) a s:Label inside it with left="10", right="10", textAlign="right"
I would hope that flex calculates the width for the label to be 80 and my text stays right aligned within those bounds with the freedom to grow vertically as I haven't set the Height or maxDisplayedLines properties. But that is not the case!
Expand/Collapse Example



Inspecting the properties at runtime via flexspy shows that the width has set to an obscene number like 10000 and if I change the text to be really really long, it simply overflows its boundaries from the right side! Which is a bit of a surprise because it goes against my intuition of having textAlign="right", which I had hoped, would at least make the overflow happen towards the left side. Even setting maxDisplayedLines="1" does not truncate the text because it thinks that it can grow up to a width of 10000 before it has to honor that property.

So apparently the right way to do this is to give up on the idea that width will be calculated based on the gap between left and right.

So if instead, for the s:Label, you specify:
a) right="10", textAlign="right" then you'll have text that can overflow towards the left side
Expand/Collapse Example



b) right="10", textAlign="right" and width="80" then you'll have text that will wrap and grow vertically as it gets longer.
Expand/Collapse Example



c) right="10", textAlign="right", width="80 and maxDisplayedLines="1" then you'll have text that will wrap and grow towards left until it gets too long and will then get truncated.
Expand/Collapse Example



I would say that this is really inflexible as I can never have text that grows within its left and right bounds and gets truncated only when it overflows the dynamically calculated width within those bounds... but oh well, such is life.


Monday, May 10, 2010

Flex4: Templatizing skins

With Flex4 the convention is to leave the appearance up to the skins but you may sometimes find that you are copy pasting the same skin multiple times with only minor tweaks in them. For example: different gradient colors, thicker/thinner borders etc. One of the most common scenarios is having a consistent button appearance across the app. If you follow Adobe's approach you will end up with multiple button skins containing very few changes. The proposed solution is to move commonly changed properties to a generic component class and then reference those properties in the skin.

Example: you have 2 types of buttons in you app - one with gradient fill and one with solid fill.
One way is to create GradientFillButtonSkin and SolidFillButtonSkin. Wouldn't it be nice to have only one skin and just fill it with what you wish to use - gradient or solid? You can!
<templatebutton>
    <fill><gradientcolor.../></fill>
</templatebutton>
To accomplish this we define a custom class TemplateButton:
TemplateButton extends Button {
...
[Bindable]
var buttonFill:IFill;
and in its respective skin class TemplateButtonSkin (which is probably a copy of spark ButtonSkin), you define a host component and a default fill:
[HostComponent("TemplateButton")]
<fx:declarations>
    <s:lineargradient id="defaultButtonFill">
    </s:lineargradient>
</fx:declarations>
...
<s:rect>
    <s:fill>
        {hostComponent.buttonFill!=null ? hostComponent.buttonFill : defaultButtonFill}
    </s:fill>
</s:rect>
Following this approach you can have with all types of fills: Solid, Linear, Radial, etc. and just use as following
<templatebutton skinclass="TemplateButtonSkin">
<fill>
    <solidfill.../>
</fill>
The same approach can be applied to strokes, (up, over, down states for fill and strokes), styles for text in the button and many others.

Monday, May 3, 2010

Flex4: Best Practices for working with states

Flex4 makes it very easy to write custom components:
1) The data & layout logic can be easily split between a Host Component (data-oriented) and a Skin (layout-oriented).
2) The Host Component can expose public Boolean flags to let the Skin indicate which state should be active.
3) Whenever invalidateSkinState() method is called (either by the system or explicitly by you), overriding the getCurrentSkinState() method based on the Boolean flags allows you complete control over the behaviour.

BUT ... just when we are about to become complacent, all kinds of bugs crawl out of the wood works and we are left wondering ... what happened? This is where the best-practices part comes in:
1) The number of Boolean flags should be equal to the number of states that you component has. (+/-)
[SkinState("normal")]
[SkinState("expanded")]
[SkinState("collapsed")]
[SkinState("disabled")]
...
public class CustomComponentView {
...
   private var _normal:Boolean;
   private var _expanded:Boolean;
   private var _collapsed:Boolean;
   private var _disabled:Boolean;
...
}
2) The Boolean flags themselves should be private or protected and you should expose Bindable public getters and setters for them. (+/-)
private var _normal:Boolean;
...
[Bindable]
public function set normal(value:Boolean):void {
  ...
}
public function get normal():Boolean {
   return _normal;
}
3) The setter methods should always be called with a value of true. (+/-)
There is no point in knowing a state that you don't want to be in, it is far better to know the state that you want to goto. If you wish to enforce this, then place an if statement around your code as follows:
[Bindable]
public function set normal(value:Boolean):void {
  if (value) {
     _normal = value;
     ...
  }
}
4) Each setter should toggle-off the Boolean values for all of the other flags. (+/-)
[Bindable]
public function set normal(value:Boolean):void {
  if (value) {
     _normal = value;
     _expanded = !value;
     _collapsed= !value;
     _disabled= !value;
  }
}


Sunday, May 2, 2010

Flex4: Show busy / loading state per component - going beyond the busy cursor

In Flex 4, the easiest way to indicate a busy or loading state for an application is to use the CursorManager's setBusyCursor() and removeBusyCursor() methods.

This is made even easier when all you need to do is set the showBusyCursor property of the SWFLoader, WebService, HttpService, and RemoteObject classes to automatically display the busy cursor.

But what if you need to go beyond that? What if there are multiple service calls that various components in your application need to make for their respective data sets? How can one go about clearly indicating which area of the application is ready for use and which one isn't?

Implementation:
1) BusyStateManager.as (+/-)
package com.xxx.util
{
   
    import flash.events.EventDispatcher;
    import flash.utils.Dictionary;
   
    import mx.collections.ArrayCollection;

    public class BusyStateManager extends EventDispatcher
    {
        /**
         * Keys for some commonly used components/areas
         */
        public static const ALL:String = "all";
        public static const HEADER:String = "header";
        public static const FOOTER:String = "footer";
        public static const DASHBOARD:String = "dashboard";

        /**
         * Use a string key to pull the component that
         * should be shown as busy. This really only
         * needs to happen IF a component wants to show
         * a busy state for a area bigger than itself.
         */
        private var availableComponents:Dictionary = new Dictionary(true);

        /**
         * Sometimes we may want to show a component/area
         * as busy but it might not have been added to stage
         * yet!
         * So the request will wait in this queue until:
         * a) either, the component is registered so that it
         *    may be marked as busy and removed from this queue
         * b) or, the busy state ends before the component is
         *    ever added to stage and thus it will be removed
         *    from the queue.
         */
        private var busyQueue:ArrayCollection = new ArrayCollection();

        [MessageHandler(selector="registerMyPersonalComponent")]
        public function registerComponent(event:BusyEvent):void {
            availableComponents[event.key] = event.component;
            // check if someone is waiting for this component to be marked as busy
            for each (var key:String in busyQueue) {
                if (key == event.key) {
                    //immediately mark the component/area as BUSY
                    showAsBusy(event.key);
                    //remove the key from the busy queue
                    busyQueue.removeItemAt(busyQueue.getItemIndex(key));
                    break;
                }
            }
        }

        public function showAsBusy(key:String):void {
            var value:Object = availableComponents[key];
            if (value != null) {
                //immediately mark the component/area as BUSY
                value.busy = true;
            } else {
                //add the key to the busy queue to be serviced later
                busyQueue.addItem(key);
            }
        }

        public function showAsReady(key:String):void {
            var value:Object = availableComponents[key];
            if (value != null) {
                //immediately mark the component/area as READY
                value.busy = false;
            } else {
                //remove the key from the busy queue
                busyQueue.removeItemAt(busyQueue.getItemIndex(key));
            }
        }
    }
}
2) Overlay for Skin (+/-)
<s:Group height="100%" width="100%" click="group1_clickHandler(event)" visible="{hostComponent.busy}">
        <s:Rect height="100%" width="100%" alpha="0">
            <s:fill>
                <s:SolidColor color="black" />
            </s:fill>
        </s:Rect>
        <ns:Image
            id="searchInProgressIcon"
            source="@Embed(source='/assets/icons/loading_spinner.swf')"
            verticalCenter="0" horizontalCenter="0"
        />
</s:Group>
3) Eat up the events in the skin (+/-)
    <fx:Script>
        <![CDATA[
            protected function group1_clickHandler(event:MouseEvent):void
            {
                event.stopImmediatePropagation();
            }
        ]]>
    </fx:Script>
4) Skin's Host Component should have the flag which toggles the overlay on & off (+/-)
[Bindable]
public var busy:Boolean = false;

How to use the code above:
1) Add a black rectangle with an alpha to indicate an overlay and the busy-spinner to the component’s skin class ... this is shown in the code.
2) Dispatch an Event from all the involved component’s view class to help them register themselves with unique names so that they may be marked as busy/ready as needed.
3) Have a wrapper around the server side action (for which the component should be busy).
4) In the wrapper class, call the BusyStateManager’s showAsBusy and showAsReady methods ... before and after making the service call. You can specifically ask for the busy or ready state to take effect on the desired components by using the name that you register them with.
5) Hopefully, you'll find it easy to plug it in and get it working for yourself as well ... good luck :)

Design:
1) Decide how you want to indicate the busy state to the user. (+/-)
1.1) You can add a s:Rect inside a s:Group at the end of each of your component skins
1.2) Assign the s:Rect an aplha (see-through) value of 0.25 to 0.75
1.3) Give the s:Group a click handler that will stop the propagation of any mouse events.
1.4) The users will feel as if there is a dark overlay or shield of sorts that is preventing them from interacting with the component when its busy.
1.5) You can always throw in some text like busy or loading and some sort of spinning graphic to polish it up a bit more.
2) Decide which service calls are made for individual components on the stage versus the ones that might provide data used by multiple components on the stage.
3) Also you may realize that it would be better to lock the entire application if certain service operations haven't finished, even if they are localized to only one of the components on the stage.
4) Often enough, the service calls might be made even before the components have been added to stage, for example when the application has just started. Even with a slow start, some components may still not have the data they need as their respective service calls might not have yet returned any results so they will need to be marked as busy whenever they are added to stage.
5) Certain service calls may need to mark the entire application or multiple components or individual ones as busy. Therefore, it is necessary for any and all components who participate in this process to register themselves with some sort of a busy state manager which can then toggle their busy state on & off for them.


Thursday, April 1, 2010

Flex 4: HTML support where art thou?

Flex 3 has mx:TextField, Air has mx:HTML but Flex 4 is only left with s:TextConverter which hardly matches up in terms of taking an HTML string and converting it into something that Flex can render. The gap in functionality is astounding. Follow this forum post for more details.

Also, if you need it, I found a great article that talks about stripping tags that cannot be processed anyway by the TextConverter engine: Strip Html Tags – with allowable tags

There are two different schools of thought on workarounds:
  1. Write your own html to flex converters
    1. Better HTML support for Adobe Flex 3 / Actionscript 3 / Flash
    2. HTML and Flex
  2. Use the iFrame trick
    1. Mixing HTML and Flex using IFrame
    2. FLEX – i – FRAME
    3. Finally Updated: Embedding HTML in a Flex application using an IFrame
  3. There's even an article listing the cons for one the iFrame approach: Don't Use IFrames for HTML in Flex

Thursday, March 4, 2010

Hardly a Flex-ible Journey: Moving from Flex 4-Beta 2 to a Stable Build Or Nightly Build

  1. Search and replace all instances of 4.0.0.10485 with the desired version (4.0.0.13875 in my case) which hopefully has already been published to maven or you have uploaded to your local repository.
    1. com.adobe.flex:flex-framework and com.adobe.flex.framework:compiler were updated for me
  2. Change the version of your org.sonatype.flexmojos:flexmojos-maven-plugin to be 3.6-SNAPSHOT
    1. Usually the repository for this is the following one: http://repository.sonatype.org/content/groups/flexgroup/
    2. Make DAMN sure that in your repository definition you have the snapshots enabled set to true
  3. Some of the locale related SWC files are not published on the maven repositories but you can overcome that by downloading the SDK and uploading the missing files manually to your local maven repository.
  4. Don't forget to tell your Flash Builder IDE about the SDK ... Project > Properties > Flex Compiler > Use a specific SDK: Flex 4.0 (build 13875)
  5. Replace all instances of xmlns:mx="library://ns.adobe.com/flex/halo" with xmlns:ns="library://ns.adobe.com/flex/mx" using your favorite tool, mine is pspad.
  6. Replace all instances of mx: with ns:
  7. Follow the instructions from this post to get your code compiling. Note: I said compiling ... not functioning!
  8. Update your injection libraries if you use any. I use Parsley so I went and downloaded parsley-2.2.0 and YES if you use maven then you also need to update the parsley version in pom.xml in order to use it.

Friday, February 26, 2010

How to setup a Sandbox for Flex 4 SDKs

1) Unzip the SDKs, as you download them,into the recommended directory structure shown here:
C:\apps\FlashBuilderBeta2\sdks\3.4.1
C:\apps\FlashBuilderBeta2\sdks\4.0.0
C:\apps\FlashBuilderBeta2\sdks\4.0.0.13875

2) In Flash Builder 4, add the SDK by goign to: Window > Preferences > Flash Builder > Installed Flex SDKs. Don't make it the default if you want the older stuff to still function.

3) Create you sandbox project and be sure to specifically select the newly added SDK, rather than the default.

3) Add this compiler option in your project settings (right-click on
your project-->properties-->Flex Compiler-->under additional compiler
arguments):
-use-network=false

4) You may also need to make the following modification in Flash Builder: Project->Properties->Build Path->Library Path->Framework Linkage and select 'Merged into code'.

5) Also one very important action which you must perform as pointed out here:
1) Rename the folder /frameworks/libs/player/10.0 to "10"
2) Edit the file /frameworks/flex-config.xml and remove ".{targetPlayerMinorVersion}" and save

6) If you still have problems, here are some references that might help you:
Reference A
Reference B

Tuesday, February 16, 2010

ToolTips: What could Flex 4 do better?

In UI there has been a long standing problem around what to do when some text doesn't fit a given area. The 1st reaction is to wrap the text. If it still doesn't fit, the 2nd reaction is to place a truncation indicator like "long label ..." and the 3rd reaction is to ask for a tooltip for the truncated text.

Flex 4 provides a semi-useful class Textbase to address this.

I use the word semi-useful because Flex 4 got the "when to show the tooltip" logic semi-wrong in my opinion. The tooltip shows up anytime the text has to wrap ... whereas it should really show up when the text actually gets truncated.

What is the point of a tooltip when the wrapped text happens to fit just fine within the provided area, without getting truncated? If you say maxDisplayedLines="2" and then you have:

label1 [no tooltip]

label2 which [shows tooltip]
wraps

label3 which [shows tooltip]
wraps and...

It feels very confusing as to why label2 has a tooltip and label1 does not. Unless you read this blog or figure it out for yourself you may even think that the tooltip behaviour is a bit inconsistent. So I think there's some room for improvement here.

Monday, January 4, 2010

Moving Maven from Flex 3 to Flex 4

Often you may have a dependency for Flex 3 in your pom.xml, such as:

com.adobe.flex.framework
flex-framework
3.x.x.xxxx
pom

and you may be wondering how to get up to speed with Flex 4 as you cannot find a version # to attach to the SDK you downloaded! Well you can look here: http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4 as this page lists the build # that you want to stick into the tag.

For example, after looking at the website listed above,
a) with flex 4 beta 2 you would put: 4.0.0.10485
b) with flex 4 beta 1 you would put: 4.0.0.7219

Also you can check you local installation: FLEX_HOME\sdks\X.X.X\flex-sdk-description.xml

If you have flex mojos as part of your pom also then make sure to have a look here too: https://docs.sonatype.org/display/FLEXMOJOS/How+to+set+Flex+SDK+version ... You should also know that it didn't seem to work right away, instead afterr half hour of leaving that config in place but not having it work, eclipse magically found and downloaded the right compiler version and all went well. Not sure if it had to do with network timeouts or if the stuff was just added to the maven repo very recently.