Flex, access view.data object into a variable - actionscript-3

Below is the code i'm looking at the moment.
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="data"
xmlns:model="model.*"
creationComplete="data=sqlSearch(data)"
>
In this View, it has just been pushView 'ed with data object. I need to use this data as part of a sql search.
I've used creationComplete one other time in the initial view. My understanding is, on creationComplete, whatever the function, (I'll just name sqlSearch here as the example), is run, and its return value becomes the data to be used in a List.
The error for the creationComplete line is
Multiple markers at this line:
-1137: Incorrect number of arguments. Expected no more than 0.
How should I go about this?

The creationComplete defined by you expects an event handler function.
To make some logic directly to the event use { } like in the following
creationComplete = "{data=sqlSearch(data)}"
I strongly suggest to use a handler function so you can add more logic on creationComplete.
For this take the following sample
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="data"
xmlns:model="model.*"
creationComplete="handleCreationComplete()"
>
<mx:Script>
<![CDATA[
private function handleCreationComplete():void
{
//Here is my code
data=sqlSearch(data);
}
]]></mx:Script>

Related

Value specified in SortField not being used by ArrayCollection Sort

I saw this earlier question (Flex arraycollection sorting not working) but it doesn't seem to pertain to the issue I am seeing.
I am trying to do a sort of an ArrayCollection using a custom compare function (using the example from here: Alphanumeric Sorting in AS3 )
The problem: It seems that the field name specified in my SortField instance is not being passed to the compare function – instead the objects themselves are. This seems wrong – but maybe I am misunderstanding (this question and the answers are a little confusing: Flex: Sort -- Writing a custom compareFunction?) – what is the point of specifying a field name in the SortField if it isn't going to be used?
A stripped down example is below. I am trying to sort a list of File instances. A trace statement in the compare function confirms that the File instance, not their name properties are being passed as arguments.
Do I need to customize the compare function? That is ** cough ** less that an optimal solution for reusable coding.
Update:
As often happens, I come up with a solution withing 10 minutes of posting a question. In this case using a proxy function. Still, I am wondering why the value specified in SortField isn't being used.
public function customCompare(obj1:*, obj2:*):int
{
return AlphaNumericSort.compare(obj1.name, obj2.name);
}
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
creationComplete="windowedapplication1_creationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import spark.collections.Sort;
import spark.collections.SortField;
import utils.AlphaNumericSort;
protected function windowedapplication1_creationCompleteHandler(event:FlexEvent):void
{
var directory:File = File.desktopDirectory.resolvePath("testFolder");
if (directory.exists){
var collection:ArrayCollection;
var obj:*;
var sort:Sort;
var sortField:SortField;
collection = new ArrayCollection(directory.getDirectoryListing());
sort = new Sort();
sortField = new SortField("name");
sortField.compareFunction = AlphaNumericSort.compare;
sort.fields = [sortField];
collection.sort = sort;
collection.refresh();
trace("---------AlphaNumericSort")
for each (obj in collection){
trace(obj.name);
}
trace(" ");
}
}
]]>
</fx:Script>
</s:WindowedApplication>
I have tested your code. It works fine. Only, in your code replace the below line
sortField.compareFunction = AlphaNumericSort.compare;
to sortField.compareFunction = customCompare;

MXML: Default MXML for Different Views

I have the following default mxml configuration.
<s:ViewNavigatorApplication
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
firstView="Home"
creationComplete="init()"
>
Is it possible to have a conditional value for firstView?
I was looking for a way to implement my application in 3 different views for mobile compatibility.So I would like to create different packages for each views. Is there any workaround for this?
You can define the views manually by using the ViewNavigator - remove the firstView from your MXML and do something like that in your init() method:
private function init():void
{
if(something)
{
navigator.pushView(Home);
}
else
{
navigator.pushView(OtherView);
}
}
// pass myData as data to the new view (will be accessible as .data property in the Home view):
navigator.pushView(Home, myData);
// remove the last view from the viewstack:
navigator.popView();
This article might help

How can I make Flex Busy Indicator load early?

I'm building a mobile app with Flash Builder 4.6 and AIR.
My Main.mxml class -- a ViewNavigatorApplication -- references a splash screen set to a minimum time of 6 seconds. After the splashscreen disappears, there's a period of a couple of seconds during which the content area of the screen is just blank white -- and that's when I need the Busy Indicator to appear.
EDIT: Additional Code, as requested:
In the Main.mxml class:
protected function viewnavigatorAppInitializeHandler(event:FlexEvent):void
{
//checks various criteria to determine which data to load, ie which service call
// to make. When that is determined, code calls setUpModel().
}
private function setUpModel():void {
Model.Instance.initialize(); //adds listeners in the model,
//makes service call. Data is returned and parsed in the event handler.
//APPLICATION_MODEL_LOADED event is then dispatched, and handled here
Model.Instance.addEventListener( Model.APPLICATION_MODEL_LOADED, modelReadyHandler );
}
private function modelReadyHandler(e:Event):void
{
//Busy Indicator only appears *after* the HomeListView is loaded.
//I need it to appear *while* these calls are being made.
navigator.pushView(HomeListView); //this is my First View
}
So I think the question is: what is it that is being displayed before HomeListView? If it's Main.mxml -- the ViewNavigatorApplication -- then how do I avoid the 'not assignable' error?
In HomeListView:
[Bindable] private var categoryList:ArrayCollection = new ArrayCollection();
private function viewActivateHandler():void
{
//the service call has been made, and data is ready to be loaded into the control
categories = Model.Instance.Categories;
I've tried this inside HomeListView:
<s:Group width="100%" height="100%">
<s:BusyIndicator
id="busy"
visible="true" symbolColor="blue"
horizontalCenter="0" verticalCenter="0"
rotationInterval="100" />
</s:Group>
It works. But the problem is it only appears once HomeListView has loaded. I thought that the blank white was HomeListView, just before the list appeared. But it isn't.
So then I tried putting the above code into the Main.mxml file. But I got the following error:
'spark.components.Group' is not assignable to the default property, 'navigationStack', of type 'spark.components.supportClasses.NavigationStack'.
So where can I put this BusyIndicator so that it covers the blank space before HomeListView appears?
I've figured out a way to do this.
From the documentation:
"Unlike Application, ViewNavigatorApplication is not meant to accept UIComponent objects as children. Instead, all visual components should be children of the views managed by the application."
That is why I was getting the error. So for my first View, I created a simple View called HolderView which contains the BusyIndicator:
<?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="HolderView">
<s:Group width="100%" height="100%">
<s:BusyIndicator
id="busy"
visible="true" symbolColor="blue"
horizontalCenter="0" verticalCenter="0"
rotationInterval="100" />
</s:Group>
Then, from my Main.mxml file, I first load that HolderView.
private function setUpModel():void {
Model.Instance.initialize();
navigator.pushView(HolderView);
Model.Instance.addEventListener(Model.APPLICATION_MODEL_LOADED, modelReadyHandler );
}
Once the data has come back -- on the Model.APPLICATION_MODEL_LOADED event listener -- that is when I will load HomeListView:
private function modelReadyHandler(e:Event):void
{
navigator.pushView(HomeListView);
}
This works. Hope it helps someone with same problem!

Flex: preventDefault on spark:ListBase not working

The proposed UX I am trying to achieve is this:
user clicks menu item (via a listBase subclass: e.g. ButtonBar or TabBar)
prevent initial selection
validate if user needs to address issues (e.g. unsaved data on a form, etc.)
if valid, take selection and set the listBase to that selectedIndex, otherwise present warnings to user and cancel out the selection process altogether
This does not work as you'd expect. Utilizing the IndexChangeEvent.CHANGING type and the preventDefault works to kill the selection, but at step 4, when I am programmatically setting the selectedIndex of the listBase, it then tries to redispatch the CHANGING event (this despite what the API docs claim).
Here is a sample application src code if you'd like to try this for yourself. I look forward to your comments & solutions.
Thanks.
J
http://snipt.org/vUji3#expand
<?xml version="1.0" encoding="utf-8"?>
<s:Application minWidth="955" minHeight="600"
xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
import flash.utils.setTimeout;
import mx.core.mx_internal;
import spark.events.IndexChangeEvent;
use namespace mx_internal;
[Bindable]
private var doPreventDefault:Boolean;
[Bindable]
private var delayMS:uint = 500;
private function buttonbar_changingHandler( event:IndexChangeEvent ):void
{
// TODO Auto-generated method stub
if ( doPreventDefault )
{
event.preventDefault();
setTimeout( delayedLogic, delayMS, event.newIndex );
}
}
private function delayedLogic( index:int ):void
{
//disabling this results in an endless loop of trying to set the selected index
// doPreventDefault = false;
//this should NOT be hitting the changing handler since we're supposed to be dispatching a value commit event instead.
bb.setSelectedIndex( index, false );
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:layout>
<s:VerticalLayout horizontalAlign="center"/>
</s:layout>
<s:ButtonBar id="bb"
changing="buttonbar_changingHandler(event)">
<s:dataProvider>
<s:ArrayList>
<fx:String>btn 0</fx:String>
<fx:String>btn 1</fx:String>
<fx:String>btn 2</fx:String>
</s:ArrayList>
</s:dataProvider>
</s:ButtonBar>
<s:CheckBox label="preventDefault?"
selected="#{ doPreventDefault }"/>
<s:NumericStepper maximum="5000" minimum="500"
stepSize="500" value="#{ delayMS }"/>
</s:Application>
Looking at the SDK, the IndexChangeEvent.CHANGING event is actually preventable - despite the documentation here says that cancelable is false, so my bad on that (although ASDoc went a little sideways), however things get a little interesting from here.
In ListBase #1296 this is only ever dispatched from the commitSelection(dispatchEvents:Boolean = true) method. In ButtonBarBase:dataProvider_changeHandler() is the only place that specifically calls to not dispatch the event, but in ListBase, it's called in commitProperties #939 when there is a proposedSelectionIndex.
So from your code above, if you are trying to set the selection - this is going to call the commitSelection, which I believe is causing the call stack issue. The Timer delay is just going to exacerbate the issue, since at 500ms the UI will have gone through its invalidation cycle at least once, meaning the commitSelection will be executed again because of an invalidateProperties flag is being set from the proprosedSelectionIndex eventually stemming from setSelectedIndex #729
So how to fix this.
I would look at only doing the prevent if the validation fails, otherwise allow it to proceed as normal. If it does fail, call the prevent, set an errorString or equivalent, but don't attempt to change the selection.
[edit] Read RiaStar's comment, and I just concurred with the same 'solution'.

How to run a function after the canvas has changed in ActionScript 3.0?

Please understand: I am a total beginner at Actionscript and anything like Actionscript, and though I generally have some idea of what I'm doing as a programmer, I'm having some setbacks in self-learning this language and Flex Builder. Right now I'm trying to make a very simple implementation where a label effectively has its text added to multiple times over the first few seconds that the program is run. The problem I'm running into is that I can't just put all that into one function + one call on that function, as changes to the label's text are apparently not seen until that function runs its full course.
So I tried using a viewstack and cloning the canvas a few times, giving each canvas's version of that label a different bit of text. Then I set the initialize function for the viewstack to change the canvases on regular intervals over the first few seconds. But that didn't work either, as the difference isn't seen until the function runs its full course.
So then I tried putting function calls on the individual canvases' initialize attributes, and those functions aren't being called at all apparently.
What the heck? I know this probably isn't even the way that you're supposed to animate something in ActionScript or Flex, but I still would like to know how to approach the problem this way for future reference. What am I missing? Thanks!
As you've noticed, changes to the displayed output of your program can't happen in the middle of executing a function that you've written. ActionScript is single-threaded, which means that none of the framework code that updates the screen can run while your function is running.
If you're interested in learning exactly what happens in order to update the screen, do a search for "flex component lifecycle" and read some of the stuff you find. It's a bit advanced, but it was the thing that really helped me understand how the Flex framework works.
Anyway, on to your real question - how to animate something. There are many ways, but for your case of progressively adding text to a label, I'd probably use the Timer class.
Here is an example:
<?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" minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
private var timer:Timer;
private var words:Array = ["The", "quick", "brown", "fox"];
private function startTimer(event:FlexEvent):void
{
timer = new Timer(1000, 1);
timer.addEventListener(TimerEvent.TIMER, updateText);
timer.start();
}
private privatefunction updateText(event:TimerEvent):void
{
theLabel.text += words.shift() + " ";
if (words.length > 0)
{
timer.reset();
timer.start();
}
}
]]>
</fx:Script>
<s:Label id="theLabel" text="" creationComplete="startTimer(event)"/>
</s:Application>
A basic approach would be to use data binding for the label value, and setTimeout() or setInterval() to cause the delay between updates.
Here is an example:
<?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"
initialize="init()"
minWidth="955" minHeight="600">
<fx:Script>
<![CDATA[
[Bindable]
private var displayString:String = "Detonation in ";
private var count:int = 5;
private var timer:uint;
private function init():void {
timer = setInterval(updateDisplay, 1000);
}
private function updateDisplay():void {
displayString += count + ".. ";
if (--count == 0) {
clearInterval(timer);
}
}
]]>
</fx:Script>
<s:Label text="{displayString}"/>
</s:Application>