Values of my Array are undefined outside of my function - arrays

I just started learning AS3 and Flash and I've got a question.
As you can see in the code below, I've created an Array out of Objects which I got from an XML File. Within my function "XMLread" I have no problems tracing, or using the values of the Array or what so ever. My problem is that I want to use the Array outside of the function "XMLread" and not only within. Whenever I try to access the values of "monthParams" outside of the function, it says the array is "undefined". How can I use the Array I created?
import flash.display.Loader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.DataEvent;
var monthParams= new Array;
var month= new Object();
var myLoader:URLLoader = new URLLoader();
myLoader.load(new URLRequest("Parameter.xml"));
myLoader.addEventListener(Event.COMPLETE, XMLread);
var actualMonth= 0;
function XMLread(e:Event){
var myXML:XML=new XML(e.target.data);
myXML.ignoreWhitespace=true;
for (var xmli:int=0; xmli < myXML.monat.length(); xmli++) {
month.number=myXML.month[xmli].number.text();
month.namen=myXML.month[xmli].namen.text();
month.mid=myXML.month[xmli].mid.text();
month.devi=myXML.month[xmli].devi.text();
month.incli=myXML.month[xmli].incli.text();
month.korr=myXML.month[xmli].korr.text();
monthParams.push(month);
}
trace (monthParams[actualMonth].mid); // works fine
}
trace (monthParams[actualMonth].mid); // doesn't work

It's because the xml load is an asynchronous operation, but the code isn't. Which means that first you start to load the xml, and immediately after it (on the very last line of code) you try to trace the value. But your xml is still not loaded. Then, at some point, the xml gets loaded, and XMLread is executed, so the trace there prints the now populated array.

Related

IndexedDb dynamically populating existing ObjectStores

I have created many, let's say three ObjectStores inside a defined and versioned IndexedDB schema.
I need to populate all of them. To do so, I created an object which stores both name end endpoint (where it gets data to populate).
Also to avoid error when trying to fill objectstores already populated, I use the count() method to ... count key inside the objectstore and if there are 0 key, then populates, else reads.
It works perfect if I execute on a one by one basis, that is instead of using a loop, declare and execute each one of the three objectstores.
However when invoking the function to populate each storage inside a loop, I get the following error message for the last two objectstores to be populated:
Failed to read the 'result' property from 'IDBRequest': The request
has not finished. at IDBRequest.counts.(anonymous function).onsuccess
Here is the code:
// object contains oject stores and endpoints.
const stores = [
{osName:'user-1', osEndPoint:'/api/1,
{osName:'user-2', osEndPoint:'/api/2},
{osName:'user-3', osEndPoint:'/api/3}
];
// open db.
var request = indexedDB.open(DB_NAME, DB_VERSION);
// in order to dynamically create vars, instantiate two arrays.
var tx = [];
var counts = [];
var total = [];
// onsuccess callback.
request.onsuccess = function (e) {
db = this.result;
for(k in stores) {
tx[k] = db.transaction(stores[k].osName).objectStore(stores[k].osName);
counts[k] = tx[i].count();
counts[k].onsuccess = function(e) {
total[k] = e.target.result;
// if the counting result equals 0, then populate by calling a function that does so.
if (total[k] == 0) {
fetchGet2(stores[k].osEndPoint, popTable, stores[k].osName); //
} else {
readData(DB_NAME, DB_VERSION, stores[0].osName);
}
};
} // closes for loop
}; // closes request.onsuccess.
The fetchGet2 function works well inside a loop, for example the loop used to create the objectstores, and also has been tested on a one by one basis.
It looks like an async issue, however I cannot figure how to fix the problem which is to be able to populate existing objectstores dynamically, avoiding to populate filled objectsores and only filling empty ones.
Indeed testing without the count issue, but inside the loop works perfect, or with the count but without loop.
At the moment and when logging counts[k], It only logs data for the last member of the object.
Thanks in advance, I'm coding with vanilla js, and I'm not interested in using any framework at all.
Yes, this looks like an issue with async. For loops iterate synchronously. Try writing a loop that does not advance i until each request completes.

How to replace content element in an Array Controller

Currently working on part of the app that requires cloning an element from the content, then modifying cloned element and saving back to model. I am having a problem when saving the cloned element and having to replace the old item with cloned one. What I am currently doing is changing all the properties of the old item like so (it works):
Blocks.replace = function(item1, item2) {
for(var k in item2) {
Ember.set(item1, k, item2[k]);
}
};
var selectedEmployment = this.get("controllers.employmentDataEntry").get("selectedEmployment");
var modelItem = content.findBy("#id", selectedEmployment["#id"]);
Blocks.replace(modelItem, selectedEmployment);
I'm trying to use the ArrayController replaceContent method, but I get an error saying "Invalid array length" when trying to run the following code:
var employmentIndex = content.indexOf(modelItem);
this.replaceContent(employmentIndex, 0, selectedEmployment);
Am I doing this incorrectly? Is there a better way of replacing an item?
Note: I am using JSON as a model. The ArrayController that is used when calling replaceContent contains an array of length 2.
Fix: Need to send in an array to replaceContent method. So change selectedEmployment to [SelectedEmployment]. Also, change 0 to 1, otherwise, content will end up having both element and cloned element.
this.replaceContent(employmentIndex, 1, [selectedEmployment]);

LoaderMax: setting array as a container (ImageLoader)

So, I have a LoaderMax instance loading images from various URLs. I want to add all loaded images to an array.
Here's my code:
var photosArray:Array = new Array(5);
var imageLoadingQueue:LoaderMax = new LoaderMax({name:"mainQueue", onComplete:completeHandler});
for (var g:uint=0; g<5; g++)
{
imageLoadingQueue.append(new ImageLoader("/img" + g + ".jpg", {name:"photo", container:photosArray[g], noCache:false, smoothing:true, width:126, height:126, scaleMode:"proportionalOutside"}));
}
imageLoadingQueue.load();
private function completeHandler(e:LoaderEvent):void
{
trace("finished loading pictures!");
//the next two lines will return an error (saying that photosArray[1] is null)
stage.addChild(photosArray[1]);
photosArray[1].x = 250;
}
A few problems:
If I set the container of the image being loaded to the Array, it won't work. I'm not being able to access the image inside the array because it says it's null.
If I set the container of the image being loaded to "this" (using the container property when appending a new ImageLoader) and, on the completeHandler, set my array equal to event.target.content, it kinda works (but it's not the ideal). The problem is that, by doing so, the images are appearing on the stage as they are loaded, and I do no want them to do so.
Any help would be heavily appreciated.
Thanks!!
David is correct, but I also wanted to mention that the LoaderMax's "content" is actually an array of all of its children's content, so you could just use that for simplicity. Keep in mind that ImageLoaders automatically create a Sprite (technically called a "ContentDisplay") to drop the image into so you probably don't need to create ANOTHER Sprite (a container for the container).
var photos:Array = imageLoadingQueue.content;
stage.addChild(photos[1]);
The other nice thing is that it creates the ContentDisplay Sprites immediately, even before any content is loaded into them, so you can place them and size them however you want while (or before or after) loading occurs.
The container needs to be a DisplayObjectContainer. ImageLoader will try to add the image to the container using addChild(), so obviously this won't work with an empty array. Create a new Sprite for each image and add it into the array first:
for (var g:uint=0; g<5; g++)
{
photosArray[g] = new Sprite();
imageLoadingQueue.append(new ImageLoader("/img" + g + ".jpg", {name:"photo", container:photosArray[g], noCache:false, smoothing:true, width:126, height:126, scaleMode:"proportionalOutside"}));
}

Accessing instance properties inside of an array

I've imported several images into an actionScript 3 document. I've turned them all into symbols (movie clips) and given them instance names to reference from ActionScript.
Ok, so I'm putting the instances into an array so I can loop through them easily, but for some reason, whenever I'm putting in the instance name, I do a trace on the value in the array and it's giving me the symbol object back, rather than the instance object.
Basically trying to loop through the array to make each instance's visibility = false
Here's a sample:
var large_cap_extrusion_data: Array = new Array();
large_cap_extrusion_data[0] = large_cap_extrusion_menu_button;
large_cap_extrusion_data[1] = extrusion_border_large_cap
large_cap_extrusion_data[2] = "Large Cap";
large_cap_extrusion_data[3] = large_cap_main_menu_button;
var extrusion_data: Array = new Array();
extrusion_data[0] = large_cap_extrusion_data;
trace(extrusion_data[0][0]);
The traces gives:
[object large_cap_menu_button]
(the parent symbol)
rather than:
"large_cap_extrusion_menu_button"
I'd be very grateful if someone could tell me where I'm going wrong...
when you trace and object, by default it describes it type. What you want is the "name" property of the object.
Try this:
trace(extrusion_data[0][0].name);
that should give you the instance nema of the large_cap_menu_button rather than the class description. Either way, you have the right object I bet.

flex 3: Can anybody help me optimize this array -> arrayCollection function?

I'm using a parent to pass a multi-dimensional array to a child. Structure of the array, named projectPositions is as follows (with example data):
projectPositions[0][0] = 1;
projectPositions[0][1] = 5;
projectPositions[0][2] = '1AD';
projectPositions[0][3] = 'User name';
I need to take this inherited array and turn it into an arrayCollection so that I can use it as a dataProvider. Currently, my init function (which runs onCreationComplete) has this code in it to handle this task of array -> arrayCollection:
for (var i:int = 0; i < projectPositions.length; i++)
{
tempObject = new Object;
tempObject.startOffset = projectPositions[i][0];
tempObject.numDays = projectPositions[i][1];
tempObject.role = projectPositions[i][2];
tempObject.student = projectPositions[i][3];
positionsAC.addItemAt(tempObject, positionsAC.length);
}
Then, during a repeater, I use positionsAC as the dataprovider and reference the items in the following way:
<mx:Repeater id="indPositions" dataProvider="{positionsAC}" startingIndex="0" count="{projectPositions.length}">
<components:block id="thisBlock" offSet="{indPositions.currentItem.startOffset}" numDays="{indPositions.currentItem.numDays}" position="{indPositions.currentItem.role}" sName="{indPositions.currentItem.student}" />
</mx:Repeater>
This all works fine and returns the desired effect, but the load time of this application is around 10 seconds. I'm 99% sure that the load time is caused by the array -> arrayCollection for loop. Is there an easier way to achieve the desired effect without having to wait so long for the page to load?
The issue your having loading items could be because you are using a repeater instead of a list class.
With a repeater, there will be a block created in memory, and drawn on the screen. So, if you have 100 items in your array, then 100 blocks will be created. this could slow down both initial creation and the overall app.
A list based class focuses on a technique called renderer recycling; which means only the displayed elements are created and rendered on the screen. So, depending on settings, you'd usually have 7-10 'block' instances on the screen, no matter how many items you have in your array.
change
positionsAC.addItemAt(tempObject, positionsAC.length);
to
positionsAC.addItem(tempObject);
addItemAt is causing a reindex of the collection which can greatly slow down the collection.
[EDIT]
Put this trace statement before and after the loop
take the output and subtract one from the other and that will show how many milliseconds the loop has run.
var date:Date = new Date( );
trace( date.getTime())

Resources