Reflect.field / event handling problem with HTML5

This could be a Haxe issue, an OpenFl issue, or a user error. I’m not smart enough to know and since my example entangles OpenFl EventDispatcher I’ll start here. Please let me know if I should post in Haxe’s bug reports on Github.

I use Reflect.field() to reference a function from a string. The function calls works fine when targeting Windows \ Debug. But when targeting HTML5 (or HTML5 \ Debug), functions within the function called via Reflect.field() are not found.

Haxe 4/0/2
OpenFl 9.0.2

Main.hx

package;

import openfl.display.Sprite;
import openfl.events.Event;

class Main extends Sprite
{
	private 		var testDispatch		:TestDispatch;

	public function new()
	{
		super();

		testDispatch	= new TestDispatch();
		addChild(testDispatch);
		testDispatch.addEventListener("TEST_DISPATCH", onHeardEvent);
	}

	private function onHeardEvent(event		:Event)	:Void
	{
		trace("I heard the event!");
	}
}

TestDispatch.hx

package;

import openfl.display.Sprite;
import openfl.events.Event;
import openfl.events.MouseEvent;

class TestDispatch      extends Sprite
{

    private         var _eventToDispatch        :Event;
    private         var _rndRect               :Sprite;
  
    public function new()
    {
       var funcName    :String;  
      
       super();
        _eventToDispatch    = new Event("TEST_DISPATCH", true, false);
        
		_rndRect	= new Sprite();
		_rndRect.graphics.beginFill(0x404040, 1);
		_rndRect.graphics.drawRoundRect(20, 20, 50, 50, 10, 10);
        addChild(_rndRect);
        _rndRect.addEventListener(MouseEvent.MOUSE_DOWN, dispatchIt, false, 0, true);
        funcName = "dispatchIt";
        _rndRect.addEventListener(MouseEvent.MOUSE_UP, Reflect.field(this, funcName), false, 0, true);
    }

    public function dispatchIt(event    :MouseEvent)    :Void
    {  
       trace("dispatchIt()     event.type = " + event.type.toString());
        dispatchEvent(_eventToDispatch);
     }
}

The MOUSE_DOWN listener directly uses the event handler function name.
The MOUSE_UP listener use Reflect.field() with a string containing the name of the event handler function.

MOUSE_DOWN works, MOUSE_UP doesn’t, as you can see from the trace() messages in the screen grab below. Again, this works fine when targeting Windows

I have the same problem trying to call functions within the class as I have with trying to call dispatchEvent();

In my real-world program, I can come up with something other than the Reflect.field() call to get around this.

I suppose I should try stripping out OpenFl and seeing I can reproduce the problem with just Haxe, but I’ve spent days just figuring out that Reflect.field() was causing my problems and I have to get moving on my project for now.

Any thoughts?

Thanks.

JavaScript is very particular with Reflection and function calls. I believe in this case, when you get the function from Reflect.field(…), it actually passes the function implementation from the prototype instead of the object instance. In order to properly call the function, you’ll have to pass in the calling object using Reflect.callMethod(…) instead. Check out the documentation here. When compiled to most other languages, the calling object is ignored, but it is required when compiling to JavaScript.

See the example code below:

var fn = Reflect.field(this, funcName);
fn(args); // wrong way, throws an error!
var callingObject = this;
Reflect.callMethod(callingObject, fn, args); // the correct way, will work because we passed in a calling object
1 Like

Thank you @Tamar! And thanks to @igazine too.

@Tamar that quickly made sense of it to me. I appreciate the short code sample to show me how to make it work.

A bit more documentation reading on Reflect and I might have come up with that on a my own–in a few hours/days.

I was using the technique as part of my quick and dirty scheme during development to throw functional buttons on the screen that are defined in XML–including the name of the function that could should be called. That let’s me quickly try different things. I was going to punt on it, but I’m glad to be able to continue to use it.

Cheers

1 Like

@igazine, your solution is the winner! Thank you so much.

I printed out “this” as @igazine suggested, and indeed when dispatched I was getting the prototype as @Tamar suggested.

I tried many forms of @Tamar’s approach, but as an argument for addEventListener(), Reflect.callMethod() made the call right then and there. The call was not made on MOUSE_UP.

@igazine’s approach was:

Add a variable to your TestDispatch class like: private var _theFunc:MouseEvent -> Void;
Assign dispatchIt to _theFunc like: _theFunc = dispatchIt;
change funcName  = "_theFunc";

That works!

My kludge gets slightly messier, because for each of my XML defined buttons, I have to have a variable in my code such as "theFunc and then use the string “_theFunc” in my XML instead of the actual function name. But that’s okay. It lets me continue to use my overall technique for both development testing and for some remote tweaking/debugging without requiring a re-compile.

Cheers!