What can cause event-handling closures to stop working? - apache-flex

I'll try to be as concise as possible. I have a number of objects in an array, and I'm applying event listeners to each one using closures:
//reduced to the logic in question:
buttons.forEach(function(button:EventDispatcher, i:int, list:Array):void {
button.addEventListener(MouseEvent.MOUSE_OVER, function(e:Event):void {
button.filters = [button_glow_filter];
});
});
//button-specific click handlers:
buttons[0].addEventListener(MouseEvent.MOUSE_CLICK, handle_some_action);
This works perfectly for a while, until I perform an unrelated action on the UI. It's a very complex system, so I'm not really sure what is happening. I can confirm that the unrelated action has no direct effect on the object that contains the buttons or the buttons themselves (at least, it's not changing anything via the public interfaces). The buttons still exist, and the click event listeners still work correctly because those are individually assigned real functions on the class's interface.
My question therefore is: does anyone know what can cause these closures to stop handling the MouseOver events without having any other perceptible effect on the related objects?
There are a number of ways to accomplish this MouseOver behavior, and for now I've switched to one that works, but I'd still like to know the answer to this question for future reference.

I figured out the likely culprit almost immediately after posting: garbage collection. It took just a couple of minutes to confirm. This is exactly what the useWeakReference parameter is for in the addEventListener interface; it defaults to true. By setting it to false, it prevents listeners assigned in this fashion from being garbage collected.
The correct code is:
buttons.forEach(function(button:EventDispatcher, i:int, list:Array):void {
button.addEventListener(MouseEvent.MOUSE_OVER, function(e:Event):void {
button.filters = [button_glow_filter];
}, false, 0, false);
});

Related

Meteor collection update focus and blur latency

I'm trying to save user content on blur and am encountering a weird UI freeze after the save that I've never seen before.
A simplified version of the template:
{{#each UserSession.getQuestions}}
<textearea class='question'>{{UserSession.getVal this._id}}</teaxtarea>
{{/each}}
In other words, we iterate over a cursor from the Questions collection that selects all questions a user must answer, and we populate the textarea with a value from the UserSessions collection that contains this user's response to that question. For what it's worth, the textarea is actually in its own template (there are other things to show in there so i figured best to keep them isolated).
The user can modify the content of his response, and then I have an event handler that basically does this:
"blur .question": function(e) {
var val = $(e.target).val();
var questionId = this._id;
var userSessionId = Meteor.user().getSessionId();
var modifier = {$set: {}};
modifier["$set"]["questions." + questionId + "] = val;
UserSessions.update({_id: userSessionId}, modifier);
}
The update succeeds but the browser freezes for about a second after the save executes, so that if the blur is triggered by a user clicking on another element, that element doesn't come into focus cleanly. This is a problem because I also want to do periodic saves while the user is typing, but when I do that, the hiccup interrupts the typing: the resulting experience is pretty brutal.
The interesting thing is that the browser only seems to freeze when the property being updated is an object or in a list. In other words:
UserSessions.update({_id: userSessionId}, {$set: {"questions.questionId": "someVal"}}) causes the freeze, as does UserSessions.update({_id: userSessionId}, {$set: {"lastSavedAt": new Date()}}).
However, UserSessions.update({_id: userSessionId}, {$set: {"someOtherProp": "someVal"}}) works fine.
I've tried several different approaches, including an async client-side pattern and executing the save via Meteor Method in a if (Meteor.isServer) block -- all the same result. Must be something to do with the publication updating, but it happens even when i set the publish function query to {reactive: false}.
I'm out of ideas. Your help, as always, is greatly appreciated.
Thanks in advance,
db
Ok I think I figured it out. For anyone else who has this problem, if you're using Iron Router you should check it for un-necessary dependencies. In this case it was the router being re-run that clogged up the works, because the data hook included a reactive find request on the UserSessions collection. I set reactive to false and specified the fields to prevent a recomputation, and everything smoothed itself out.

Meteor subscribe onReady() and observe() added double counted

I want to wait for all data to be downloaded from the subscription and then create map markers for them all at once at the beginning. To do this, I have a session variable set to false. Then when onReady calls, I initialize all the markers. Then I set the session variable true indicating that the first delivery is in and initialized. In my observe callback, I check the session variable and so long as its false, I dont add any markers. Then, if its true, I will add markers -- assuming non of these markers are already initialized. Sometimes, however, I get a double-count and create twice as many markers.
I guess a good first question to ask is what the relationship is between onReady and observe added? Its not terribly clear in the docs. Is this even the correct way of doing things -- creating a session variable to suppress the observe added function until onReady is done? I dont think so. Also note that the double count doesnt happen every time so its a timing thing... kind of annoying.
Thanks
Yes this is the behavior with observe(). When you run observe initially it will have an initial query that will match everything and run into added.
It is also present when onReady hasn't yet fired but the collections are empty at that point so the initial ones aren't visible. This is mentioned in the docs
Before observe returns, added (or addedAt) will be called zero or more times to deliver the initial results of the query.
I'm not sure entirely how to avoid the initial items. I have done something like this in the past:
var QueryHandle = Collection.find().observe({
added: function() {
if(!QueryHandle) return false;
});
I know this works on the server but I'm not certain if it does on the client.
Another solution would be to run the handle before onReady is called and only stop returning if the subscription is complete: i.e
Meteor.subscribe("collection", function() {
subscribed = true;
});
var QueryHandle = Collection.find().observe({
added: function() {
if(!subscribed) return false;
}
);
Be careful not to run this in a Deps.autorun because then it would be run again if the .find() query params are reactive.
This might happen sometimes depending on how fast the server response. If you use Session it becomes a reactive hash so if it happens fast enough that subscribed returns true. Try using an ordinary variable instead.
If its not helpful there might be an alternative way to avoid the initial ones and a deeper level but it might take a dig into the livedata package.

suppress events for Flex objects

[Edit]
The main question here loosely translates as 'is Flex multi-threaded'? I have since found out that it is not, so I won't have data mysteriously changing half way through an operation. The code below worked, but made things awkward and confusing. I eventually fixed the problem with an architecture change, eliminating the need to suppress events. As the first commenter suggested.
Infinite loops were eliminated by changing the way events were listened to and performing certain actions explicitly rather than via events.
Collating events was made easier using a command pattern.
Basically, do not use the code below if you come across this page!
[/Edit]
I'm building some Flex applications using a simple, lightweight MVC pattern. Models extend or encapsulate a dispatcher and fire events when updated. I'm stuck with Flex 3.5.
In some situations, I'll want to suppress these events to avoid infinite loops or help collate multiple actions into a single event.
My first stab at a solution that doesn't litter the models with unnecessary and confusing code is this:
private var _suppressEvents:Boolean = false;
public function suppressEvents(callback:Function):void
{
// In case of error, ensure the suppression is turned off, then re-throw
var err:Error = null;
_suppressEvents = true;
try
{
callback();
}
catch(e:Error)
{
err = e;
}
_suppressEvents = false;
if (err)
{
throw (err);
}
}
public function dispatch(type:String, data:*):void
{
// Suppress if called from a suppress callback.
if (!_suppressEvents)
{
_dispatcher.dispatchEvent(new DataEvent(type, data));
}
}
Obviously I call 'suppressEvents' with a function containing the model code I wish to run.
My questions:
1: Is there a chance I could accidentally lose events using this technique?
2: Do I need to think about any other error edge cases when it comes to ensuring I don't accidentally end up in a suppressed state after a call?
3: Is there a cleaner way I'm missing?
Thanks!

AIR Application enabled=false not processed (Busyindicator)

In my AIR application (with mate-Framework) i did follwing things:
click on a button
call a method in my model "onApplicationBusy"
apply some filter in arraycollections.
In my onApplicationBusy there is this code:
FlexGlobals.topLevelApplication.enabled = false;
FlexGlobals.topLevelApplication.
I trace every step and all methods are called in right order.
But my application never becomes disabled.
Why. Is there a method for this purpose.
I try InvalidateDisplayList or ValidateNow or callLater. But all tries won't work. Probably i try it on the wrong place?
I assume, my application is so busy while applying filters (4 values for 10.000 lines) that the disabled property can't processed.
If i call the method without applying the filters all works fine.
If i call just the disbaled property but never enable the app again, the app will shown as disabled after applying the filters. for me too late.
What i origin want is a clear behavior, when the app is busy and when not (ready for clicking on buttons and all this stuff).
If you can help me or know a method, how can i shown a busy application, please help me
Thanks
Frank
All right, the setTimeout Method solve my issue. I assume, I have to wait for the next screen refresh.
Why callLater won't work and when i have to implement those functions, because i have too less ressources while my filterFunction is running?
Frank
I'm using FlexGlobals.topLevelApplication.stage.mouseChildren = false | true;
Worth noting that I first tried setting the mouseEnabled flag but found various visual elements would not update at all while mouseEnabled = false.

stack overflow on XMLListCollection collectionEvent

I'm working on a Flex 3 project, and I'm using a pair of XMLListCollection(s) to manage a combobox and a data grid.
The combobox piece is working perfectly. The XMLListCollection for this is static. The user picks an item, and, on "change", it fires off an addItem() to the second collection. The second collection's datagrid then displays the updated list, and all is well.
The datagrid, however, is editable. A further complication is that I have another event handler bound to the second XMLLIstCollection's "change" event, and in that handler, I do make additional changes to the second list. This essentially causes an infinite loop (a stack overflow :D ), of the second lists "change" handler.
I'm not really sure how to handle this. Searching has brought up an idea or two regarding AutoUpdate functionality, but I wasn't able to get much out of them. In particular, the behavior persists, executing the 'updates' as soon as I re-enable, so I imagine I may be doing it wrong. I want the update to run, in general, just not DURING that code block.
Thanks for your help!
Trying to bind the behaviour to a custom event rather than the CHANGE event.
I.e. do what you are doing now, but dispatch and handle a custom event to do the work.
Have you considered using callLater?
Does direct manipulation of XMLListCollection's source XMLList have the same results?
Have you considered something like:
private function changeHandler( event:Event ):void
{
event.target.removeEventListener( event.type, changeHandler );
// your code here.
event.target.addEventListener( event.type, changeHandler );
}

Resources