remove event listener as3 - arrays

I'm almost finished with a quiz of mine. I've encountered an error which I have finally pinpointed. But first I'll tell you some background info on the code.
Background
On stage, there exist 4 button components registered as movie clips. These are later stored in an array. When a certain icon is clicked on stage, these buttons are to be activated by adding event listeners to all of them. When a button is pressed, it checks if it's the 'correct' answer and removes the event listeners.
Now I have done extensive checks and narrowed down the problem to the following.
Code
This function will add button listeners to each button recursively. Note that the variable 'num' is a fixed integer between 1 - 4 which is generated earlier into the code and used for many 'if' cases.
function addButtonListener(num:int):void
{
for (var obj:Object in _buttons)
{
_buttons[obj].addEventListener(MouseEvent.CLICK, checkans(num));
}
}
This function is basically the opposite and also disables the buttons
function removeButtonListener(num:int):void
{
for (var obj:Object in _buttons)
{
_buttons[obj].removeEventListener(MouseEvent.CLICK, checkans(num));
_buttons[obj].enabled = false;
}
}
Now one thing I noticed through the use of trace functions is that the code correctly adds the button listeners but it does not remove them.
This function calls for each button to be removed.
function checkans(num:int):Function
{
return function(e:MouseEvent):void
{
if (e.currentTarget.label == xmlNodes)
{
points = points + (2 * num);
scoreBox.text = points.toString();
}
else
{
trace("Incorrect!");
}
if(myText.parent){
myText.parent.removeChild(myText)
}
closeShowQuestion(num);//closes a previous function
removeButtonListener(num);//Should I pass this variable into the end function?
GoFishing.addEventListener(MouseEvent.CLICK, fish);//Starts the process again.
}
}
So am I removing the event listeners incorrectly?
Do you need to see more code to be sure?
Thanks in advance!

removeEventListener must use the SAME function that was used in addEventListener.
Your checkans function always returns a NEW function, so it will not be the same for add and remove.
If you have to pass a num into your listener function, do it other way. Make it a property of a button or store externally etc.

Related

Creating dynamic buttons on codenameone

I am trying to make it so when i click a button another is created, and you can do this as many times as you want too. But I haven't been able to find a way to make it work, any ideas? I have tried creating a for loop but that just ends up overwriting the other buttons and deleting the tags.
Try something like this:
Form f = new Form(BoxLayout.y());
f.add(createButton("Click Me"));
f.show();
Then the method createButton():
private Button createButton(String title) {
Button b = new Button(title);
b.addActionListener(e -> {
Container c = b.getParent();
c.add(createButton(title));
c.revalidate();
});
return b;
}
I'm guessing the thing you missed is the call to revalidate() which must be invoked when you change a Form after it was already shown. Notice the first addition occurs before the form is shown and doesn't invoke revalidate().

angularJS - can you watch a function call?

wondering if there is a way in angular to watch a function like you would a variable
function foo() {
console.log("foo called");
}
$scope.$watch('foo', function {
console.log("foo watched");
});
So given the syntax is correct, will "foo watched" be printed to the console, if not, is there a way to have that watch function run?
EDIT
Reformatting question to more of what I need:
Let's say I have a button and an text input, each handled by different controllers, say btnCtrl and txtCtrl
There is also a boolean global flag stored as $scope.flag which changes the validation standards for the input.
//btnCtrl
//called on button click
function onButtonClick() {
$scope.flag = !$scope.flag
}
//txtCtrl
//called on input change
function onInputChange() {
handleValidations()
}
function handleValidations() {
//set error indicator on invalid input
//valid input changes based on $scope.flag value
if($scope.flag) {
//check validation1
} else {
//check validation2
}
}
I want to check the input box validations, handleValidations when $scope.flag is changed. $scope.flag is changed frequently not by the button, but by other functions.
So, I would like to call handleValidations not when $scope.flag is changed, but when onButtonClick is called. What is the best way to do that?
My initial thought was to put some sort of $scope.$watch on onButtonClick.
You can watch the functions. Have you tried running your code? If you call foo, it should print both console statements. There is a 'gotcha' for watching functions though, mastering $watch in Angularjs
One key aspect to note here, is that if the expression is evaluated as a function, then that function needs to be idempotent. In other words, for the same set of inputs it should always return the same output. If this is not the case, Angular will assume that the data being watched has changed. In turn, this means that it will keep detecting a difference and call the listener at every iteration of the digest cycle.
With the help of the comments and other answers:
I sent a local message using $rootscope from btnCtrl and added a listener to txtCtrl that calls handleValidations
Thank you all for the help

mdbottomsheet disable drag down to close

I would like to disable the drag down to close gesture of mdbottomsheet. I've found a work around on scripts but I'm not sure where to put the code. Thanks for the help.
As you say that angular-material doesn't provide any option to disable it, obviously you will have to make changes in its source code.
Now, you haven't mentioned whether you want to disable it at specific places or turn drag-down-to-close for bottomSheets everywhere.
1) In case of latter, it would be quite straightforward, as the only thing you need to do is remove the event listeners for drag events.
If you use angular-material.js file, heres what you can do:
Find the function BottomSheet(element, parent). This function basically registers the drag events which close the sheet. We need make it not attach the listeners.
Reduce it to:
function BottomSheet(element, parent){
return {
element: element,
cleanup: angular.noop
};
}
The cleanup function basically de-registers the listeners on drag event.This function is called when the scope of the bottomSheet is destroyed. To make minimal changes, we have just reduced the cleanup function to do nothing.
2) If you want to be able to pass an option while creating the sheet in your controller, you do the same thing, but conditionally based on the option you pass. Wont write the code because I assume you know how angular works, but here are the steps:
=> Add a boolean variable along with other options(template,scope,etc. ). Lets call it dragDownToClose.
=> In the defaults injector function inside the provider function of MdbottomSheet , assign it a default value (true/false).
=>Pass this along with element and parent during instantiation of BottomSheet() inside the onShow function.
=> So BottomSheet() will now have three argument - dragDownToClose being the new one.
=> As we did in the former case, return the element without any handler attached when the value is false, and let the original function be when its true.
Of-course there are various ways in which you can actually implement this. However, I hope you get the idea.
First, inject $element into your controller. You known what AngularJS $element do, right?
Then we both known that the drag events are registered in BottomSheet
parent.on('$md.dragstart', onDragStart)
.on('$md.drag', onDrag)
.on('$md.dragend', onDragEnd);
So, the simple solution is: Remove those events, override those events... without override the function BottomSheet, right?
$element
.on('$md.dragstart', function (ev) {
return false;
})
.on('$md.drag', function (ev) {
return false;
})
.on('$md.dragend', function (ev) {
return false;
});
Something still wrong here! The backdrop still draggable! So, we do the same for backdrop
var parent = $element.parent();
var backdrop = parent.find('md-backdrop');
backdrop
.on(blah blah blah
These is code in case you are asking for
You can try
$mdBottomSheet.show({
template: *yourTemplate*,
clickOutsideToClose:false
})
this will not let the user close the bottom sheet even with drag or click outside.

setting value of html select element in change handler doesn't work on ios6

I'm using an html select element as an active menu. When you select an item from it, it does an action and then in some cases, a side effect of the action is resetting the value of the menu to something else.
function onMenuChangeHandler() {
var menu = $('#menu');
var menuChoice = menu.val();
if (menuChoice == ...) {
...
menu.blur(); // ensure change handler doesn't get fired again
menu.val(OTHER_VALUE); // **
}
};
This works fine on the desktop in multiple browsers and works fine on iOS5. It inexplicably stopped working on iOS6. The result is that it acts as if the line marked ** above is not there.
It works fine in any case where I don't set the value.
FYI: There's another change in iOS6 which seems unrelated to this but mentioning it just in case. When the menu is selected, it now dismisses the picker immediately rather than leaving it open. This is how it works on every other platform. If you have more more than one select element, you still get the non-standard behavior.
I spent a lot of time trying to track this down and eventually discovered that the problem is that iOS6 sets the value of the control a second time after my change handler exits. If I put in alerts, I can clearly see that the control changes to the OTHER_VALUE (underneath the alert). Then when I dismiss the alert, the control reverts. (Without the alert, the change happens too fast to see.)
So here's a workaround:
function onMenuChangeHandler() {
var menu = $('#menu');
var menuChoice = menu.val();
if (menuChoice == ...) {
...
setTimeout(function() {
menu.blur(); // ensure change handler doesn't get fired again
menu.val(OTHER_VALUE);
}, 1);
}
};
Maybe someone else has a better answer or a better explanation.

extjsGirdPanel.getView().getRowClass does not work 2nd time

The handler of an extjsAction button calls a function with following code.
Add: function() {
var window = Ext.getCmp('wndAdd');
window.items.items[0].getStore().reload;
var Grid1 = Ext.getCmp('grdAll');
var grdStore2 = Ext.getCmp('grid2').getStore();
var i = 0;
var IDList = new Array();
for (i = 0; i < grdStore2.data.length; i++) {
IDList[i] =
grdStore2.data.items[i].data['ID'];
}
Grid1.getView().getRowClass = function(record, index) {
if (IDList.contains(record.data["ID"])) {
return 'disabled-row';
}
};
window.show();
}
But the getRowClass function works only on the first button click. does ot disable the row which gets added.
getRowClass only needs to be assigned one time. It is not a function that you call, it is a function called internally by the grid every time a row is rendered. Rather than assigning it inside an event handling function, it should be assigned ONE time, somewhere at the application level (e.g., wherever Grid1 itself is first configured would be the most logical place). This may or may not be your issue, depending on how your Add function is getting called, which is not clear. Bear in mind that since you rely on IDList inside getRowClass, you'll also have to have a reference to that variable that is in scope where the function is, and you will probably also have to add checks to make sure it is valid before accessing it.
You are also not showing where Grid1 is getting re-rendered. As explained above, getRowClass only executes when rows are rendered, so unless you are refreshing Grid1 somewhere not shown in your code, getRowClass will never be called.
FYI, while I'm glad that you found a solution that works for you, I'm not sure you understand getRowClass still. It does NOT get called only once -- it gets called EVERY time a grid row is re-rendered (which is anytime data changes). It was only getting called once in YOUR code because your code was not set up correctly.
I don't fully understand your use case, but regardless of the window being shown, the row striping would only need to change if the grid's underlying data actually changed. When you set up the getRowClass function properly, the grid takes care of calling it for you automatically, when it needs to, and it should "just work." There should be no need for your iteration code as written which just adds extra overhead.
Again, just FYI. :)
Yes, the getRowClass gets called only once when the grid is configured. But I wanted something that would fire everytimewindow.show() gets fired. I used the below code on window.onshow event.
for (var i = 0; i < Grid1.getStore().data.length; i++) {
var element = Ext.get(Grid1.getView().getRow(i));
var record = Grid1.getStore().getAt(i);
if (IdList.contains(record.data.ID)) {
element.addClass('disabled-row')
} else {
element.removeClass('disabled-row')
}
}
I will edit my response with an appropriate answer once supplied more information.
When are you firing getRowClass? It seems like you are creating the function but never actually calling to for a response.
Does the function error out, get called at all, or just doesn't do what you want?

Resources