Haxe JS: createElement "mapped" to custom class

180 views
Skip to first unread message

Thomas John

unread,
Jul 27, 2016, 7:57:22 PM7/27/16
to Haxe
Hi,

I might be explaining it wrong but here is what I would like to do:

I have a class "SpecialItem" that extends DivElement.
When I instantiate it with "new SpecialElement();", it is not a true HTML element. It is just an ordinary object with the properties of a Div element.
I would like it to be a Div Element that I can append to the dom.

Is this possible ?

Thanks

Thomas.

Philippe Elsass

unread,
Jul 28, 2016, 1:21:56 AM7/28/16
to Haxe

DOM elements can only be created using `document.createElement`.

Nowadays there seem to be ways to create a custom prototype for it but it won't be very Haxe-friendly:
http://www.html5rocks.com/en/tutorials/webcomponents/customelements/

Philippe


--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
---
You received this message because you are subscribed to the Google Groups "Haxe" group.
For more options, visit https://groups.google.com/d/optout.

Thomas John

unread,
Jul 28, 2016, 8:44:43 AM7/28/16
to Haxe
Thanks for the confirmation.
createElement would seem to be able to do the trick I think. Maybe haxe could do the registerElement call automatically somewhere in its initialization phase when it detects a class extends another html element.
But it is clearly not supported by enough browsers to even think about it.
Thanks again for your quick answer.

Mark Knol

unread,
Jul 29, 2016, 3:19:56 PM7/29/16
to Haxe
This can be done with abstracts. This is not really extending but is quite nice.

http://try.haxe.org/#d164A

Thomas John

unread,
Jul 29, 2016, 3:50:38 PM7/29/16
to haxe...@googlegroups.com
wow, i'm gonna try this, this looks perfect !
thx a lot.

On Fri, Jul 29, 2016 at 9:19 PM, Mark Knol <mark...@gmail.com> wrote:
This can be done with abstracts. This is not really extending but is quite nice.

http://try.haxe.org/#d164A
You received this message because you are subscribed to a topic in the Google Groups "Haxe" group.

For more options, visit https://groups.google.com/d/optout.



--
Thomas John

Philippe Elsass

unread,
Jul 30, 2016, 2:57:15 AM7/30/16
to Haxe
Amazing, I need to jump on the abstract bandwagon :)

You received this message because you are subscribed to the Google Groups "Haxe" group.

For more options, visit https://groups.google.com/d/optout.



--
Philippe

Thomas John

unread,
Jul 31, 2016, 8:59:08 AM7/31/16
to haxe...@googlegroups.com
abstract classes can't have member variables... making it unfortunately pointless for what I need.
I understand Haxe creates an object (or a "static" class) in which functions are accessed statically with a "this" argument.

Wouldn't it be possible to actually make the abstract type (or maybe another kind of type) a real class that can be instantiated as many times as we actually instantiate it in the code ? so we can have member variables and functions can access those member variables without too many tricks.
Below is an example of what I wish it could do:

  • The abstract type:

abstract SpecialDiv (js.html.DivElement) to js.html.DivElement {
    
    public var specialVar:String = "test";
    
    public inline function new () {
        this = js.Browser.document.createDivElement ();
        this.innerText = "hey";
        toto(["tutu", "titi"]);
    }

    public function toto(s:Array<String>)
    {
        this.innerText = specialVar + "," + s.join(",");
    }
}

  • And the resulting javascript would be this:

var SpecialDiv = function() {
    this._abstractedObject = Browser.document.createDivElement();
    this._abstractedObject.innerText = "hey";
    this.toto(["tutu", "titi"]);
};

SpecialDiv.__name__ = true;
SpecialDiv.prototype = {
    toto: function(s) {
        this._abstractedObject.innerText = this.specialVar + "," + s.join(",");
    }
    ,__class__: SpecialDiv
};

As you can see, it automatically separates the abstracted object and puts it in a member variable.
So every time we access that object via the "this" keyword, haxe replaces the "this" with the member variable ("_abstractedObject" in this case)
and if, for example, we would append that object to the dom like this:
someDiv.appendChild(specialDivInstance);
it would become:
someDiv.appendChild(specialDivInstance._abstractedObject);

etc...

Or maybe this could be done with macros ?
do you have any advice for macros ?

Thanks.

szczepan

unread,
Jul 31, 2016, 10:28:49 AM7/31/16
to Haxe
Well, back in the day they called it 'composition' and some even claimed it's better than inheritance. ;-)

Just make a regular class which takes an html element in the constructor (or creates) and make all access fields 'inline' - same effect.

Thomas John

unread,
Jul 31, 2016, 10:37:11 AM7/31/16
to haxe...@googlegroups.com
would you have any example ? I don't understand the "make all access fields inline".
thx !

On Sun, Jul 31, 2016 at 4:28 PM, szczepan <szcze...@gmail.com> wrote:
Well, back in the day they called it 'composition' and some even claimed it's better than inheritance. ;-)

Just make a regular class which takes an html element in the constructor (or creates) and make all access fields 'inline' - same effect.
--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Haxe" group.
For more options, visit https://groups.google.com/d/optout.



--
Thomas John

Justin L Mills

unread,
Jul 31, 2016, 11:40:33 AM7/31/16
to haxe...@googlegroups.com

On 31/07/2016 15:37, Thomas John wrote:
> would you have any example ? I don't understand the "make all access
> fields inline".
> thx !
>
You might find this example of "using" is perhaps another similar approach.

https://haxe.org/manual/lf-static-extension.html

If you put inline in front of the static methods then the function will
be automatically copied in the place of use by the compiler making for
sometimes faster code.

you can read about inline

https://haxe.org/manual/class-field-inline.html

Probably ideal to do your own experiments to get a feel for how this all
fits together, but it allows you to have features of complex inheritance
frameworks without the restrictions and limitations and overhead of
inheritance.

szczepan

unread,
Jul 31, 2016, 1:54:26 PM7/31/16
to Haxe
I'm sorry Thomas. Actually, I was wrong, you can't really use a composition here (for the same reasons you can't just extend those native elements).

I think the most straightforward approach is to make a wrapper class but this forces you to define every field (repeatedly) that you want to allow an user to use.

There's also another problem, that is will your class really extend an html element in a browser, ie. will you be able to add your custom class object into a DOM? If it doesn't, than all you can get is a syntax sugar by the use of abstract/wrapper classes (or 'using', indeed). Otherwise, there're aformentioned custom elements with all the cross-compatibility hell. :)


BTW. I have an idea, that I hasn't been able to test yet, of making a map of DOM objects to wrapping them custom class objects, which should make it possible to pass your custom object into a DOM functions with help of an 'abstract' class. Going from DOM into your code would require a look-up into the mappings and return wrapping object (with native DOM element in it).

Thomas John

unread,
Jul 31, 2016, 7:12:53 PM7/31/16
to haxe...@googlegroups.com
I've tried many things and it seems to be impossible to do what I want unless you put raw javascript code which I don't want to.

Also I read in many places that extending prototypes is something one should really avoid as there is nothing in the javascript implementation that certify that all browsers will actually take the changes into account (and I think I came across something similar in FramerJs on an android device)

In the end I chose to stick with the wrapper approach. It's not that less practical than I thought it would be.

Anyways, thank you all for trying to solve that problem with me.

Cheers

Thomas.

--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Haxe" group.
For more options, visit https://groups.google.com/d/optout.



--
Thomas John

Benjamin Dasnois

unread,
Jul 31, 2016, 7:27:29 PM7/31/16
to haxe...@googlegroups.com
I had experimented with custom elements and Haxe and it is actually possible and somehow straight-forward but then you still have to overcome all the painful things from custom elements…
--

Jeff Ward

unread,
Aug 1, 2016, 6:29:08 PM8/1/16
to Haxe


On Sunday, July 31, 2016 at 6:59:08 AM UTC-6, Thomas John wrote:
abstract classes can't have member variables... 

It's time for another "abstracts are lame and never really do what you want them to do" post. :P Actually, they're great... as long as what you want to do is basically the one thing they were designed to do.

Ok, ok, complaining aside...

I don't think you have to alter the built-in prototypes (which is the bad practice I think you're referring to.)  I think you could simply assign your class properties (basically the functions) to your div instance, and get everything you're after: https://gist.github.com/jcward/88d427d383a3c2feb3e5627f1cfdb85a

You have to ditch using the new() constructor (because once you're in a function constructor, you can't create and return an element) and use a factory instead, wherein you instantiate a div and mixin your class properties. There are surely limiting factors in this implementation (e.g. property default values are no longer supported since they're set in the constructor, which is never called), but my quick test seems to show it basically working (even with Type.getClass() -- because the __class__ property is mixed in).

Is this not basically what you're trying to do?

Cheers,
-Jeff

Thomas John

unread,
Aug 1, 2016, 8:11:47 PM8/1/16
to haxe...@googlegroups.com
I don't think I ever said "abstracts are lame".

As for your implementation, yes :), it is what I was looking for !

I just added a couple of things so it actually takes property default values (i'm not a pro in js so what I did might not be good in all situations...)
Now I wonder if we can actually make a macro out of this to make the whole thing more "seamless" ? I have never done any macro and, honestly, if someone knows how and thinks it's feasible, I would just need some pointers (but some good ones :)) and I could try.

  • the new code:

import js.Browser;
import js.html.Element;

class SpecialElement extends Element
{
    public var myValue:String = "toto";
   
    private function new()
    {
        // nothing here, but you can do things if needed
    }
   
    // some function you can call from the "outside" and that returns something back
    public function test(data:Array<Dynamic>):String
    {
        Browser.console.log("calling test with:", data);
        Browser.console.log("myValue:", myValue);
        return data.join(",");
    }
   
    // a unique static instance of the class to copy the property values from
    public static var specialElementInstance:SpecialElement = new SpecialElement();
   
    // the static function to call to create a new instance
    public static function createNewInstance():SpecialElement
    {
        var elem = Browser.document.createElement('div');
            untyped __js__('
            var idx;
            var props = Object.getOwnPropertyNames(this.prototype);
            // getting props from prototype
            for (idx in props) {
                var prop = props[idx];
                elem[prop] = this.prototype[prop];
            }
            // getting props from instance of class
            props = Object.getOwnPropertyNames(this.specialElementInstance);
            for (idx in props) {
                var prop = props[idx];
                elem[prop] = this.specialElementInstance[prop];
            }
        ');
       
        return untyped elem;
    }
}

  • try it with this:

var se:SpecialElement = SpecialElement.createNewInstance();
Browser.console.log("calling special element function:", se.test(["hello", 2, "ça va ?"]));

  • and it should give:

calling test with: Array [ "hello", 2, "ça va ?" ]
myValue: toto
calling special element function: hello,2,ça va ?


Cheers !


--
To post to this group haxe...@googlegroups.com
http://groups.google.com/group/haxelang?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Haxe" group.
For more options, visit https://groups.google.com/d/optout.



--
Thomas John

Jeff Ward

unread,
Aug 2, 2016, 11:34:40 AM8/2/16
to Haxe
Hi Thomas,

I'm not accusing you of saying abstracts are lame. I personally am saying abstracts are lame and never do what I envision. :)

As for a macro, you could potentially make the whole process more transparent, so that any class that extends Element (since we know this is not possible) automatically gets the behavior. I haven't yet written a macro where "all calls to X get changed into Y" -- is there an example for that? I wonder if you could also invoke the constructor with .apply(my_div, constructor args);

But my problem with macros is that they are ultra slow. They will start to slow your build to a crawl doing anything complex. Which is sad, because the Hxe compiler is otherwise very fast.

Best,
-Jeff

Thomas John

unread,
Aug 2, 2016, 12:02:48 PM8/2/16
to haxe...@googlegroups.com
oh sorry, I misunderstood you, but this is not a problem anyways :)

If someone has a clue on where to start to transform this into a macro, I take it !

Thanks again.

David Mouton

unread,
Aug 7, 2016, 5:17:58 AM8/7/16
to Haxe
Hi,
We made a library to use custom elements easier.
http://happy-technologies.com/custom-elements-and-component-developement-en/

Reply all
Reply to author
Forward
0 new messages