Dynamic String Interpolation

197 views
Skip to first unread message

Tristan McNab

unread,
Aug 8, 2012, 9:52:24 PM8/8/12
to mi...@dartlang.org
I'm trying to build a server-side mvc framework and generating views based on templates and whatnot, and I was wondering if I could apply Dart's string interpolation dynamically. For example, this would be my view template:

<!DOCTYPE html>

<html>

  <head>

    <meta charset="utf-8">

    <title>${ViewData["Title"]}</title>

    <link href="/Content/css/site.css" rel="stylesheet" />

  </head>

  <body>

    <h1>${ViewData["Title"]}</h1>

    <div id="container">

      <p>Hello world!</p>

    </div>

  </body>

</html>


And I'd like to apply the ViewData variable using:

static String applyViewData(String html, Map ViewData) {

    // apply interpolation here

}


Is this at all possible at the moment? My searching of the APIs indicate that it isn't.


-Tristan.

Seth Ladd

unread,
Aug 8, 2012, 9:55:01 PM8/8/12
to mi...@dartlang.org
Sorry, Dart doesn't let you do this. However, you might be interested in this Mustache port to Dart: https://github.com/iggymacd/MustacheDartPort

I haven't tried it, but I hopefully it works. Do let us know if you give it a shot!


--
Consider asking HOWTO questions at Stack Overflow: http://stackoverflow.com/tags/dart
 
 

Seth Ladd

unread,
Aug 8, 2012, 9:55:45 PM8/8/12
to mi...@dartlang.org
I also found this template engine for Dart: https://github.com/zhygr/edt

Tristan McNab

unread,
Aug 8, 2012, 9:59:08 PM8/8/12
to mi...@dartlang.org
Perhaps I should try log a feature request for a String instance method called String.interpolate(context)?

William Hesse

unread,
Aug 9, 2012, 4:10:11 AM8/9/12
to mi...@dartlang.org
By wrapping the string literal in a function that takes the context as
a parameter, you can have a Function : context -> String that you can
pass around instead of a String. If you need to use some String
operations, like concat, on these objects, you can implement these
operations on a class encapsulating this type ("lifting" them). This
seems like a straightforward way to give the string literal in one
place, and give the data you want to interpolate in another.

String interpolation always happens dynamically, each time the literal
is used, and the data can easily come from a parameter to a function
rather than from the lexical context.

For example:
Function MyTemplate() {
return (Context context) {
return "<table><tr><td class=${context.leftColumnClass}>Red
Sox</td><td>${context.data}</td></tr></table>";
}
}

...
var templateHere = MyTemplate();
...
var output = templateHere(context);


You could also skip a level of indirection and just create
String FillMyTemplate(Context context) => '''
<html><head><title>$context.title</title></head>
''';
and use FillMyTemplate where you need the template.
> --
> Consider asking HOWTO questions at Stack Overflow:
> http://stackoverflow.com/tags/dart
>
>



--
William Hesse

Kevin Kellogg

unread,
Aug 9, 2012, 6:57:51 AM8/9/12
to mi...@dartlang.org
It doesn't really answer your question but you might be interested in how they are using mirrors for bindings in this Web Components library. It's client side and looks to be very much a work in progress.

Kevin

Sam McCall

unread,
Aug 9, 2012, 9:16:27 AM8/9/12
to tri...@seditious-tech.com, mi...@dartlang.org
The reason this doesn't work is an interpolated string is short-hand for an expression, and is processed at compile time.

E.g. "Foo: $bar" compiles to a javascript expression like ("Foo: " + bar) and since JS is lexically scoped you can't easily change what bar is dynamically.

Referring explicitly to a context object like Bill suggested is a good workaround this, but there's a trick involving noSuchMethod() if you really don't like that:

class Template {
  var _context;

  noSuchMethod(method, args) {
    if (!method.startsWith("get:")) return super.noSuchMethod(method, args);
    return _context[method.substring(4)];
  }

  abstract String template();

  String evaluate(context) {
    _context = context;
    try {
      return template();
    } finally { _context = null; }
  }
}

class MyTemplate extends Template { template() => """
  <title>$title</title>
  <h1>$title</h1>
""";}

final renderedText = new MyTemplate().evaluate({"title": "Hello, world"})

Seth Ladd

unread,
Aug 9, 2012, 11:33:12 AM8/9/12
to mi...@dartlang.org, tri...@seditious-tech.com
Awesome, thanks Bill and Sam! This was too good to keep in the mailing list, I copied the answers into this Stack Overflow question: http://stackoverflow.com/questions/11886796/can-i-apply-darts-string-interpolation-dynamically


--

Justin Fagnani

unread,
Aug 9, 2012, 12:01:20 PM8/9/12
to mi...@dartlang.org, tri...@seditious-tech.com

I think everyone so far is missing a key point of Tristan's question: it appears that his strings are not known at compile time, so they really do need to be parsed.

Justin

Sam McCall

unread,
Aug 9, 2012, 1:06:53 PM8/9/12
to mi...@dartlang.org, tri...@seditious-tech.com
On Thu, Aug 9, 2012 at 6:01 PM, Justin Fagnani <justin...@google.com> wrote:

I think everyone so far is missing a key point of Tristan's question: it appears that his strings are not known at compile time, so they really do need to be parsed.

That's impossible, by design: the expression inside ${...} is arbitrary dart code, and dart doesn't have eval().

I'm not sure why the strings wouldn't be known at compile time - in a typical server-side MVC framework, they are known at compile time, but writing them as literal strings inside a .dart file isn't optimal.

Much nicer if you can reload the page and see changes in the templates, without recompiling/restarting the server though...

Cheers,
Sam

Alan Knight

unread,
Aug 9, 2012, 1:55:51 PM8/9/12
to mi...@dartlang.org
Yes, that last one is what I'd do. It's similar to what we're doing with messages and internationalization.
Reply all
Reply to author
Forward
0 new messages