Friday, March 30, 2012

Dart Templates (Bleeding Edge)

‹prev | My Chain | next›

When I wake up later today, I plan to do nothing but edit Dart for Hipsters. But of course, now is not the time to displease the gods of my chain for they have been so good to me. So before I get a few hours of sleep, I make a small offering in the form of messing about with the template stuff in Dart's bleeding edge.

I already have much of bleeding edge checked out from the other night when I was trying out dartdoc. But I do not have the utils sub-directory. So I again install subversion:
sudo apt-get install subversion
Then in the same directory in which I checked out the other bits of the SDK, I grab utils:
➜  dart  svn checkout http://dart.googlecode.com/svn/branches/bleeding_edge/dart/utils
A    utils/apidoc
A    utils/apidoc/html_diff.dart
A    utils/apidoc/.gitignore
...
Checked out revision 6072.
Then I remove subversion on principle:
sudo apt-get remove subversion
With that, I am ready to templatize something!

In my sweet Hipster MVC-based comic book application, I already have a poor man's template defined:
  _singleComicBookTemplate(comic) {
    return """
      <li id="${comic['id']}">
        ${comic['title']}
        (${comic['author']})
        <a href="#" class="delete">[delete]</a>
      </li>
    """;
  }
This seems like a nice candidate on which to experiment.

So I extract that out into ComicBook.tmpl as:
template ComicBook(comic) {
  <li id="${comic['id']}">
    ${comic['title']}
    (${comic['author']})
    <a href="#" class="delete">[delete]</a>
  </li>
}
Next, I try compiling that into Dart code with the template utility:
➜  dart-comics git:(modal-dialog) ✗ ~/repos/dart/utils/template/template public/scripts/ComicBook.tmpl 
:1:20: fatal: Template paramter missing type and name
template ComicBook(comic) {
                   ^^^^^
Parsed /home/cstrom/repos/dart-comics/public/scripts/ComicBook.tmpl in 63 msec.
Codegen /home/cstrom/repos/dart-comics/public/scripts/ComicBook.tmpl in null msec.
Wrote file /home/cstrom/repos/dart-comics/public/scripts/ComicBook.dart in null msec.
Ugh. Does it really need to know that code is an instance of HipsterModel? Can it even find that class if it needs it? There is an easy way to find out and that is adding the type declaration:
template ComicBook(HispterModel comic) {
  <li id="${comic['id']}">
    ${comic['title']}
    (${comic['author']})
    <a href="#" class="delete">[delete]</a>
  </li>
}
With that, it compiles:
➜  dart-comics git:(modal-dialog) ✗ ~/repos/dart/utils/template/template public/scripts/ComicBook.tmpl
Parsed /home/cstrom/repos/dart-comics/public/scripts/ComicBook.tmpl in 60 msec.
Codegen /home/cstrom/repos/dart-comics/public/scripts/ComicBook.tmpl in 12 msec.
Wrote file /home/cstrom/repos/dart-comics/public/scripts/ComicBook.dart in 12 msec.
To get that back into my comic book view, I need to #source() (pull directly into library namespace) the generated template code:
#source('ComicBook.dart');
Looking through the generated code, it seems like I need to access the result of the template on the root property:
Element get root() => _fragment;
The problem with that is that, at the point that I extracted this from a working application, I was building HTML strings, not Element objects. So, to use this, I need to replace my previous String function:
    var html = '';
    list.forEach((comic) {
      html += _singleComicBookTemplate(comic);
    });
With an outerHTML call on the root Element:
    var html = '';
    list.forEach((comic) {
      html += (new ComicBook(comic)).root.outerHTML;
    });
Loading that up in Dartium, however throws an error:
Internal error: 'http://localhost:3000/scripts/ComicBook.dart': Error: line 20 pos 56: unterminated string literal
    var e0 = new Element.html('<li id="${comic['id']}">
                                                       ^
Looking at the generated output, I see that it does seem incorrect in that a multi-line string is declared with single quotes:
  ComicBook(this.comic) : _scopes = new Map<String, Object>() {
    // Insure stylesheet for template exist in the document.
    add_ComicBook_templatesStyles();

    _fragment = new DocumentFragment();
    var e0 = new Element.html('<li id="${comic['id']}">
    ${inject_0()}
    (${inject_1()})
    </li>');
    _fragment.elements.add(e0);
    var e1 = new Element.html('<a href="#" class="delete">[delete]</a>');
    e0.elements.add(e1);
  }
And, indeed, adding triple quotes fixes that problem:
  ComicBook(this.comic) : _scopes = new Map<String, Object>() {
    // Insure stylesheet for template exist in the document.
    add_ComicBook_templatesStyles();

    _fragment = new DocumentFragment();
    var e0 = new Element.html('''<li id="${comic['id']}">
    ${inject_0()}
    (${inject_1()})
    </li>''');
    _fragment.elements.add(e0);
    var e1 = new Element.html('<a href="#" class="delete">[delete]</a>');
    e0.elements.add(e1);
  }
I am unsure if that single quote would work in bleeding_edge Dart or not. Regardless, triple quotes definitely eliminates the error.

That eliminated the unterminated string problem, but now I get:
Internal error: 'http://localhost:3000/scripts/ComicBook.dart': Error: line 19 pos 21: type 'DocumentFragment' is not loaded
    _fragment = new DocumentFragment();
                    ^
My Hipster MVC is so well encapsulated, that I have not needed the HTML library yet, which is why DocumentFragment is not defined. To gain access to it, I #import() the HTML library into the view class:
#import('dart:html');
#source('ComicBook.dart');
With that, I have my compiled template working in my Hipster MVC view:


Problems with bleeding edge aside, I cannot say that I am sold on these templates. This was a somewhat contrived, small example. The pseudo-template with which I started seems more appropriate. No doubt this would have worked better on a larger example, but I really go out of my way to keep my views small.

The use of safeHMTL under the template covers is encouraging. That said, the compilation step is painful. Perhaps templates will get first-class support before long. I would also prefer to define most templates inside my view classes. Assuming both of those concerns are addressed, then I can see using these templates. Until then, I will stick with multi-line strings.


Day #342

No comments:

Post a Comment