Tuesday, January 17, 2012

Dart Ajax Take Two

‹prev | My Chain | next›

Last night, I came oh-so-close to getting XHR requests from a Dart web page working. To be precise, I was able to get HTTP GET requests to flow via XHR, but POSTs eluded me. Tonight, I pick back up, starting with some tips passed along by Adam Coding in last night's comments.

The first thing that I try is setting the request headers. I do not think that this will help as the error that I am getting is that send() is not implemented, but fingers crossed. In yesterday's comic book application, I now specify that I am passing application/json:
    var data = {'title':title.value, 'author':author.value}
      , json = JSON.stringify(data);

    print(json);

    var req = new XMLHttpRequest();
    req.open('post', '/comics', true);
    req.setRequestHeader('Content-type', 'application/json');
    req.send(json);
    print(req.responseText);
Unfortunately, but not too surprisingly, I am still greeted with a not-implemented exception when I trigger the XHR send by submitting the form:
Exception: NotImplementedException
Stack Trace:  0. Function: 'XMLHttpRequestImplementation.send' url: '/home/cstrom/repos/dartium/src/out/Release/obj/gen/webkit/bindings/XMLHttpRequestImplementation.dart' line:43 col:3
 1. Function: 'XMLHttpRequestWrappingImplementation.send' url: 'dart:htmlimpl' line:25561 col:20
 2. Function: '::function' url: 'http://localhost:3000/scripts/comic_put.dart' line:27 col:13
 3. Function: 'EventListenerListImplementation.function' url: 'dart:htmlimpl' line:23183 col:35
Bummer.

My next step is to try this from the command-line version of Dart that I have. I reduce my browser script to the following:
#import('dom');
#import('dart:json');

main() {
    var data = {
          'title':"Watchmen",
          'author':"Alan Moore"
        }
      , json = JSON.stringify(data);

    print(json);

    var req = new XMLHttpRequest();
    req.open('post', 'http://localhost:3000/comics', true);
    req.setRequestHeader('Content-type', 'application/json');
    req.send(json);
    print(req.responseText);
  });
}
Unfortunately, that also fails to work. It seems that the command-line version of Dart lacks the dom, html, and json libaries:
➜  command_line git:(master) ✗ dart xhr01.dart
'/home/cstrom/repos/dart-site/examples/command_line/xhr01.dart': Error: line 1 pos 1: library handler failed: Do not know how to load 'dart:html'
#import('dart:html');
^
Again bummer.

The last thing that I can try is compiling the Dart code into Javascript:
scripts git:(master) ✗ frogc comic_put.dart 
/home/cstrom/local/dart-sdk/lib/htmlimpl/htmlimpl.dart:23094:21: warning: a map literal takes one type argument specifying the value type
    _listenerMap = {};
                    ^^^^^^
➜  scripts git:(master) ✗ ls -1
comic_put.dart
comic_put.dart.js
That warning message is a bit weird, but seems to be ignorable.

After switching my web page to use the compiled Javascript instead of the pure Dart:
<script src="scripts/comic_put.dart.js" type="application/javascript"></script>
Then things work. My express.js + node-dirty backend responds with:
{"title":"Watchmen","author":"Alan Moore","id":"12e5c6c33b16399f778bad86fc6bc082"}
And, indeed, checking the node-dirty store, my new record is indeed in place:
➜  comix git:(master) ✗ node
> db = require('dirty')('comix.db')
> db.get('12e5c6c33b16399f778bad86fc6bc082')
{ title: 'Watchmen',
  author: 'Alan Moore',
  id: '12e5c6c33b16399f778bad86fc6bc082' }
For the sake of completeness, the POST route in my express.js backend looks like:
app.post('/comics', function(req, res) {
  var graphic_novel = req.body;
  graphic_novel['id'] = dirtyUuid();

  db.set(graphic_novel['id'], graphic_novel);

  res.statusCode = 201;
  res.send(JSON.stringify(graphic_novel));
});
And yes, externally they might look like comic books, but I will always think of them internally as graphic novels. ANYhow...

One caveat with the compiled Javascript is that it is not executed on DOM-ready like the equivalent Dart code. With the Dart version, I could put the <script> tag before the form HTML. Once I converted to the compiled Javascript, I had to move the <script> tag after the HTML—otherwise the attempt to add an on-submit event handler failed because the Javascript could not find the form element.

I am a bit surprised that the version of Dartium that I have does not support XHR send() with a data argument. The frogc dart-to-javascript compiler is older than Dartium and yet it supports sending data via XHR. I will likely recompile Dartium with the latest source code changes included and follow up on the mailing list in the upcoming days.

In the end, a few NotImplemented exceptions are to be expected for such a hipsterish language. If nothing else, it is pretty cool to know that the Dart code that I quickly threw together last night was sufficient to read form values and submit them via XHR. That is a definite win for Dart.


Day #268

No comments:

Post a Comment