How to increment a global variable inside a function? - apache-flex

The variable currentIndex is declared globally and initialized with a certain value say '0'. How do I hold the value of currentIndex which is incremented every time the function is called? In the given code, every time the function is called, the value is reinitialized.
function nextSong(e:Event):void
{
sc.stop();
currentIndex = currentIndex + 1;
var nextSongFunc:URLRequest = new URLRequest(songlist[currentIndex].file);
var nextTitle:Sound = new Sound();
nextTitle.load(nextSongFunc);
currentSound = nextTitle;
sc = currentSound.play();
sc.addEventListener(Event.SOUND_COMPLETE, nextSong);
}
NextBtn.addEventListener(MouseEvent.CLICK, nextSong);

You need to declare the variable outside the function. How you do this depends on the context. Where is this function being defined? In the 'actions window' on the timeline in Flash? or inside a <script> block in Flex? or somewhere else?
It looks like you're in the Flash tool, in the actions window. If so, then just do it like this:
var currentIndex:int = 0;
function nextSong(e:Event):void {
sc.stop();
currentIndex = currentIndex + 1;
var nextSongFunc:URLRequest = new URLRequest(songlist[currentIndex].file);
var nextTitle:Sound = new Sound();
nextTitle.load(nextSongFunc);
currentSound = nextTitle;
sc = currentSound.play();
sc.addEventListener(Event.SOUND_COMPLETE, nextSong);
}
NextBtn.addEventListener(MouseEvent.CLICK, nextSong);
If that doesn't work, let me know some more details, and we'll sort it out.

If you're using Flash CS , you should take advantage of the DocumentClass. In such case you could define currentIndex as a private variable and it will be incremented/decremented in your functions.
This is a much better approach than writing your code in the Actions panel, allows for a lot more flexibility and you don't run into problems due to frame dependent code.

Related

Use Widget Value in Server Script

I may be overthinking this drastically, but what is the easiest way to access a widget's value in a server script?
In my particular case, I am trying to use a dropdown widget's value as the filter for a calculated model query.
function getMonthlyTotalsByResource_() {
var allRecordsQuery = app.models.Allocations.newQuery();
allRecordsQuery.filters.Approved._equals = true;
allRecordsQuery.filters.Resource.Manager.ManagerName._equals = /* How do I make the widget's value available here? */
var allRecords = allRecordsQuery.run();
...
...
In your calculated model datasource in the server script have the following:
return getMonthlyTotalsByResource_(query);
Still in your model datasource add a parameter ('String?') and call it ManagerName.
On your page with the dropdown bind the value of the widget to #datasource.properties.ManagerName
In your server script function change to the following:
function getMonthlyTotalsByResource_(query) {
var allRecordsQuery = app.models.Allocations.newQuery();
allRecordsQuery.filters.Approved._equals = true;
allRecordsQuery.filters.Resource.Manager.ManagerName._equals =
query.parameters.ManagerName;
var allRecords = allRecordsQuery.run();
The easiest way to do this is to add a parameter to the datasource of your calculated model. Then bind something to it from the client side (e.g. bind to datasource.mycalculatedds.parameters.myparam ).
Then pass the default 'query' object from your calculated datasource. For example in your calculated DS, where you call your function, call
getMonthlyTotalsByResource_(query). Then you can set something like
var thismanager = query.parameters.myparam
// get widget value
function getWidgetValue() {
var val = app.PAGES.YOUR_PAGE.descendants.WIDGET_NAME.value;
return val;
}
// execture query
function getMonthlyTotalsByResource_(widgetValue) {
var allRecordsQuery = app.models.Allocations.newQuery();
allRecordsQuery.filters.Approved._equals = true;
allRecordsQuery.filters.Resource.Manager.ManagerName._equals = widgetValue;
var allRecords = allRecordsQuery.run();
}
// run
getMonthlyTotalsByResource_(getWidgetValue);

Combine/merge Dynamic Objects in AS3

I have 2 dynamic objects and I want to build one to contain all the properties:
var o1:Object = {prop1:val1,prop2:val2,prop3:val3};
var o2:Object = {prop3:val3a,prop4:val4};
and I need to obtain a third object that looks like that:
{prop1:val1, prop2:val2, prop3:val3a, prop4:val4};
Basically I need a way to iterate through the object properties and to add new properties to the third object. I have to mention I'm quite new to AS3/Flash/Flex.
First question, do you really mean to have prop3 in both objects? you will need to decide what to do in case of a collision like that, which object has precedence.
Secondly, check out the introspection apis: http://livedocs.adobe.com/flex/3/html/help.html?content=usingas_8.html
something like this should work:
public function mergeDynamicObjects ( objectA:Object, objectB:Object ) : Object
{
var objectC:Object = new Object();
var p:String;
for (p in objectA) {
objectC[p] = objectA[p];
}
for (p in objectB) {
objectC[p] = objectB[p];
}
return objectC;
}
If the property exists in A and B, B's will overwrite A's. Also note that if the values of a property is an object, it will pass a reference, not a copy of the value. You might need to clone the object in those cases, depending on your needs.
Note: I haven't actually tested the above, but it should be close. Let me know if it doesn't work.
Updated to fix the errors. Glad it works for you though.
You can dynamically access/set properties on objects with the index operator. The for loop will itterate over the property names, so if you put it all together, the following test passes:
[Test]
public function merge_objects():void {
var o1:Object = {prop1:"one", prop2:"two", prop3:"three"};
var o2:Object = {prop3:"threeA", prop4:"four"};
var o3:Object = new Object();
for (var prop in o1) o3[prop] = o1[prop];
for (var prop in o2) o3[prop] = o2[prop];
assertThat(o3.prop1, equalTo("one"));
assertThat(o3.prop2, equalTo("two"));
assertThat(o3.prop3, equalTo("threeA"));
assertThat(o3.prop4, equalTo("four"));
}
you can iterate over the object properties like:
var obj1:Object = new Object();
for(var str:String in obj2){
obj1[str] = "any value"; // insert the property from obj2 to obj1
}

Get item by index in a tree control

I'm doing a drag and drop operation on a tree using some help from Adobe's quick Starts:
http://www.adobe.com/devnet/flex/quickstart/working_with_tree/
The code suggested is roughly this:
var dropTarget:Tree = Tree(evt.currentTarget);
var i:int = dropTarget.calculateDropIndex(evt);
myTree.selectedIndex = i;
var node:XML = myTree.selectedItem as XML;
var drugXML:XML = XML(Tree(evt.dragInitiator).selectedItem);
if(node.localName() != drugXML.localName()){
DragManager.showFeedback(DragManager.NONE);
return;
}else{
DragManager.showFeedback(DragManager.COPY);
}
This is all well and good, but I don't like the way it is selecting(highlighting) each item in the tree I'm dropping on, it's less clear where the item is going to drop. I want to implement this without the selection but there doesn't seem to be a way to get the XML of the node using just the index. I would like something like this:
var dropTarget:Tree = Tree(evt.currentTarget);
var i:int = dropTarget.calculateDropIndex(evt);
var node:XML = myTree.itemAt(i) as XML;
//AFAIK itemAt(i) or anything like it does not exist
var drugXML:XML = XML(Tree(evt.dragInitiator).selectedItem);
if(node.localName() != drugXML.localName()){
DragManager.showFeedback(DragManager.NONE);
return;
}else{
DragManager.showFeedback(DragManager.COPY);
}
So does anyone know what function is, or what I can do to extend the tree to have a function, like "itemAt(i)"
Thanks
~Mike
EDIT: I forgot to post that my current workaround is setting the selectedIndex = -1 after I get my node:XML. I'm afraid that if something bogs on the processor the user may see it select then deselect.
Much simpler, though there may be gotchas:
var index:int = ...
var renderer:IListItemRenderer = tree.indexToItemRenderer(index);
var item:Object = renderer.data;
This won't work if the index is offscreen (since there might not be an active itemRenderer); shouldn't be an issue for drag and drop.
If your dataProvider is explicitly or implicitly a collection (see the linked docs for conversion rules), you should be able to use getItemAt to resolve the index.
It appears that an XML provider will be converted implicitly:
var tree:Tree = ...;
var data:XML = ...;
tree.dataProvider = data; // could just as well be from MXML
var provider:ICollectionView = tree.collection; // tree converted it for us
trace(provider.getItemAt(index));
If you have something other than the convertible types (XML, Array, etc.), you might consider wrapping your dataProvider in an XMLListCollection or what have you to gain access to that method.
The above is aiming in the right direction but missing.
Turns out you don't want the dataProvider since Tree overrides that; you want the collection property (protected). So you could override Tree and provide an indexToItem method to go with the thisToThat methods already present in Tree.

Flex 3: How do I determine if a generic Object is actually a button?

I have this bit of code, and it's not working as I expected. btnContainer is a VBox that contains a ton of buttons (toggle=true); and I want to reset them to un-toggled at a certain point.
for (var btn:Object in btnContainer.getChildren()){
if (btn.isPrototypeOf(mx.controls.Button)){
btn.selected = false;
}
}
With the above code, "btn" shows up as just the index during each iteration of the loop (0,1,2,3,...), and the conditional never evaluates to true.
I also tried this:
for (var btn:Button in btnContainer.getChildren()){
btn.selected = false;
}
This works fine, except that there is also a label inside btnContainer; so it throws an error when trying to cast the label as a button.
What am I doing wrong, here?
If you want to loop through the elements of an array, use a "for each..in" loop, and if you want to see if a variable is compatible with a given type (e.g. an instance of a given class), use the is operator.
The language reference has an example for this exact kind of case.
Here's the fixed code:
for each (var btn:Object in btnContainer.getChildren()){
if (btn is Button){
btn.selected = false;
}
}
Have you tried using is?
import mx.controls.Button;
//...
for (var key in btnContainer.getChildren()){
var obj : Object = btnContainer[key];
if (obj is Button){
var button : Button = obj as Button;
button.selected = false;
}
}

Flex - Dispatch custom Event on receipt of standard Event

I am processing a FileReferenceList.fileList[] of multiple files a user selects in the following code..
for (i=0;i < event.currentTarget.fileList.length; i ++){
fileByteData = new ByteArray();
fileByteData = (event.currentTarget.fileList[i].data as ByteArray);
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, checkImageDimensions);
loader.loadBytes(fileByteData);
}
I need to pass i into checkImageDimensions to keep track of which image is which, I can easily enough create a custom event, but I need this to fire at the right time. Ideally I could do something like this..
var myEvent:CustomEvent = new CustomEvent(i);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.dispatchEvent(CustomEvent))
But to be honest, I am unsure of how to proceed...
Can anyone help me out? Thanks!
You need to make a listener function - a function call does not suffice. Passing a class name to dispatchEvent does not work either. Here's how to do it.
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {
dispatchEvent(new CustomEvent(i));
});
Note that you don't necessarily need a custom event class, if all you need in the event is a type string. You can simply use the Event class in this case:
public static const MY_CUSTOM_EVENT:String = "myCustomEvent";
...
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {
dispatchEvent(new Event(MY_CUSTOM_EVENT));
});
Edit: If you're really lazy, but still want to pass values with the event, you can use the DinamicEvent class:
var evt:DynamicEvent = new DynamicEvent(MY_CUSTOM_EVENT);
evt.attr1 = val1;
evt.attr2 = val2;
dispatchEvent(evt);
Of course, it is cleaner and less error prone to declare your own error class.

Resources