Vanilla Haxe JS

I’ve seen it all in JavaScript, from Netscape 4’s years of pain, to jQuery to abstract browser differences, and finally to Vanilla JS and node.js – we now live in a world where coding in JavaScript is relatively sane and consistent.

Except we still have to use JavaScript. Not that I don’t like JavaScript – I’m quite found of the language and have sympathy for its quirks, but it’s just missing something to be productive once your code becomes more that half a dozen of classes.

Here comes the Haxe language and it’s almighty type inference!

haxe-inference 
Enjoy smart Haxe completion in many popular editors


Vanilla JS?

Started as a kind of joke, Vanilla JS means working without jQuery and all those band-aid libraries because, quite frankly, they aren’t that useful nowadays if your project can target IE9+.

I enjoyed my time with jQuery but I’ve grown to prefer manipulating my DOM elements myself – it turns out the API is incredibly rich, not really complicated or more verbose, and it will run generally a lot faster than jQuery & co.

Vanilla Haxe JS?

Everything you can do in JavaScript you can do it in Haxe JS. Simple.

And you can do it better because of many little details and because Haxe is an amazing language that improves on JavaScript while being syntactically very close to it.

Compiling to JavaScript?

There must be a reason why compiling to JavaScript is incredibly popular nowadays. And even if you’re coding in “pure” JavaScript you’re probably still using compile-like steps to browserify, minify, etc. so you’re just adding an additional step.

And you will barely feel like you are compiling because the Haxe compiler is so damn fast – even hundreds of classes take a pair of seconds to compile.

A concrete example

Let’s say we want to show a list of items and a button to create new items. Simple enough but covering the different interesting bits.

Creating the view

Haxe is a strongly typed language, applying stricly type inference on all your code unless explicitly annotated as Dynamic or prefixed with untyped.

Notice how I added type annotations only where required (class members), while the rest is all inferred (and verified) by the compiler. Notice also that this is optional.

Haxe exposes the browser API through the Browser class. You can use document.createElement as usual, but Haxe adds aliases which return the right DIV/UL/LI/etc node type. Once compiled it’s all replaced by regular, inlined, createElement(tagName) calls.

var view:DivElement;
var list:UListElement;
var button:ButtonElement;

function createChildren() 
{
  var doc = Browser.document;
  
  view = doc.createDivElement();
  view.className = "listview";
  
  list = doc.createUListElement();
  
  button = doc.createButtonElement();
  button.textContent = "Add an item";
  
  view.appendChild(list);
  view.appendChild(button);
  doc.body.appendChild(view);
}

Loading JSON data

Haxe will use the built-in JSON API but you can force the inclusion of a fallback using the -D haxeJSON compiler directive (see conditional compilation).

[
	{ "label":"Item 1", "id":0, "date":"2014-10-27 12:00:00" },
	{ "label":"Item 2", "id":1, "date":"2014-10-27 14:00:00" },
	{ "label":"Item 3", "id":2, "date":"2014-10-27 16:00:00" },
	{ "label":"Item 4", "id":3, "date":"2014-10-27 18:00:00" },
	{ "label":"Item 5", "id":4, "date":"2014-10-27 20:00:00" }
]

I’m using Haxe’s crossplatform version of XmlHttpRequest. But you can obviously directly use XmlHttpRequest if you prefer, or Browser.createXMLHttpRequest() for a crossbrowser version.

Notice that you don’t need to use any trick to maintain the scope in the onData handler: you have access to the class’ data and render methods.

Notice untyped alert(err): here I explicitly disable Haxe typing to write unverified JS code. Very handy to work with external APIs for which you don’t have compiler definitions.

var data:Array<ItemInfo>;

function loadData() 
{
  var loader = new Http("data.json");
  loader.onData = function(raw) {
    try {
      data = Json.parse(raw);
      if (!Std.is(data, Array))
        throw "ArgumentError: Json data is not an array";
      render();
    }
    catch (err:Dynamic) {
      untyped alert(err);
    }
  }
  loader.request();
}

Isn’t it curious that I wrote that the data is an Array<ItemInfo>? One of the features in Haxe I absolutely love is typedef types, aka "strongly typed duck-typing" (it’s brilliant when working with JSON), so I defined the type of my JSON items as:

typedef ItemInfo = {
	label:String,
	?date:String, // optional
	id:Int
}

Rendering

Again, let’s just use Vanilla JS to render the list. I’m leveraging the document fragment API to create the list items off screen and to append them all at once.

Notice the loop iterating on the array values – it’s generated as a while loop without the nasty side effects of JavaScript’s for-in.

function render() 
{
  var doc = Browser.document;
  var fragments = doc.createDocumentFragment();
  for (info in data)
  {
    var li = doc.createLIElement();
    var label = doc.createDivElement();
    label.textContent = info.label;
    li.appendChild(label);
    fragments.appendChild(li);
  }
  list.innerHTML = "";
  list.appendChild(fragments);
}

Adding new items

Easy stuff, especially thanks to Haxe’s scope conservation and type inference.

It is important to remind that the compiler will infer everything and will report errors if the typing is wrong:

  • the onclick handler is required to have exactly one (Dynamic) parameter,
  • I’m not allowed to push in data an object that doesn’t respect the ItemInfo typedef.

Bonus feature: string interpolation to create the item’s label.

button.onclick = function(_) {
  data.push({
    label: 'New item ${data.length+1}',
    id: data.length
  });
  render();
}

Conclusion

Honestly, doesn’t it look like better JavaScript? It’s brilliant I think.

You can get the full sample project code is on Github.

Using external JavaScript libraries from Haxe

This isn’t ways easy so read my next article about working with JavaScript libraries.

Interop from JavaScript

If you want to use your Haxe JS generated code from JavaScript, simply declare your class as @:expose class MyClass and you’ll be able to write new MyClass() in your JavaScript. Easy and natural!

But, but, and jQuery?

Yes you can still use jQuery if you want – it’s in Haxe’s default JavaScript API. The only constraint is that you can’t use the $() shortcut but new JQuery().

If you want to add externs for jQuery plugins you can use Haxe’s very handy static extension methods.

7 thoughts on “Vanilla Haxe JS

  1. Pingback: Philippe.me » Y U NO Haxe? Misconceptions!

  2. Haxe JS itself is compatible with IE6+, but the DOM APIs are dependent on the browser.
    For old browsers support you can still use jQuery with Haxe, or look at quirksmode.org to know the exact browser differences.

  3. Nice write up, Haxe has everything I like about ES6, Atscript, TS, Dart, except a more powerful type system, macros, dead code elimination and all the magic in the compiler.

    Minus the => though 😉

    For me there is one thing missing and that is an ide that debugger that can use the source maps like the chrome tools do like the break points. That is a deal breaker for teams of developer’s who are more productive in an ide that has the kind of integration Javascript has in WebStorm and Visual Studio for example.

    I hope the tivo fork of the Intellij plugin can help on this, I think they have the cpp debugger working so far.

  4. Pingback: Philippe.me » Haxe: working with JavaScript libraries

  5. I am so for debugger support, this is the only missing thing, nothing else. Maybe these browser debuggers follow somme standards, have some external interfaces, I dont know, I hope so.
    A cross-platform HaxeDevelop 🙂 resurected with debugger support …

  6. @iongion
    Debugging works fine within Chrome, Firefox and IE; including with source mapping if you prefer (I prefer debugging the generated JS). It isn’t just integrated with any IDE for now.

    PS: for cross-brower compatibility of source maping you need to add the compiler option ‘-D source-map-content’.

Leave a Reply

Your email address will not be published. Required fields are marked *