Javascript + Razor == Jazor?

I’ve been doing quite a lot with Razor lately, namely still working on RazorEngine vNext, but also reading a lot of Andrew Nurse’s blog to understand what the Razor parser is doing under the hood.

Turns out, its an elegant synergy of two separate parsers, one which handles code, one which handles markup. The two parsers do a little dance, and the end result, is a series of code statements that write the content of your template/page/document to an output. This in turn is compiled into a class which is executed to obtain the end result.

Jazor (ahem), attempts to be a faithful implementation of the Razor syntax, but built purely in javascript. The general idea would be that you could declaratively use Razor syntax in your client side templates. Let’s have a quick look:

<script type="text/html" id="template01"> <div>Hello @model.name, you are @model.getAge() year(s) old!</div> </script>
Code language: HTML, XML (xml)

Now, let’s breakdown how it works:

Synergistic ballet
Andrew aptly calls this “recursive ping-pong” (read his blog post here), its an elegant way of breaking down a text stream into a recursive set of calls to each parser. We start off at the very top level, and make an initial call to a markup parser; when the markup parser starts reading the content, it will do so until it reaches a valid transtion point, the “@” symbol (well, there is a little more to it than that, we have to take email address, and @@ escaping into consideration). Once a valid transition point is reached, the markup parser calls the code parser, which in turn attempts to read a code block (an expression, statement, etc.).

The way we do this with Jazor is we have our core object, the parser which wraps a codeParser and a markupParser. The parser makes a call to the markupParser to start reading the content. and the recursive little dance begins.

Generating code
Much like Razor, we start generating blocks of code which we will reassemble as part of our template execution. If we take our above example, Jazor will generate a literal block, followed by an expression, then a literal, another expression, and then a final literal, so:

"n <div>Hello " model.name ", you are " model.getAge() " year(s) old!</div>n "
Code language: JavaScript (javascript)


We need to transform this series of blocks into something runnable, thankfully, there is eval.

Return of eval
After we’ve done generating our template blocks, we assembly them back together as a function, which we can then eval, and run. We do this by writing the code that wraps the blocks. For the above example, the code we generate would look something like:

(function(model) { var r = []; r.push("n Hello "); r.push(model.name); r.push(", you are "); r.push(model.getAge()); r.push(" year(s) old!n "); return r.join(""); });
Code language: JavaScript (javascript)


I’ve prettified the code for readability, but essentially we are writing a custom method, and then we evaluate that as a function object, which can be executed directly:

var func = eval(tmp); // Where tmp is our generated function code.
Code language: JavaScript (javascript)

Executing templates
You can start running templates using the global jazor object:

var model = { name: "Matt", getAge: function() { return 27; } }; var result = jazor.parse("Hello @model.name", model);
Code language: JavaScript (javascript)

Alternatively, if you are using jQuery, you can use the $.fn.jazor method on your query object:

var result = $("#template01").jazor(model);
Code language: JavaScript (javascript)

What does Jazor support
In this initial 0.1.0, it is really just a preview, very much unfinished. Currently we have support for most Razor syntaxes, so we have:

Expressions

@model.name
Code language: CSS (css)

Code Blocks

@{ var name = "Matt"; }
Code language: JavaScript (javascript)

Explicit Expressions

@(model.name)

Line parsing

@: Hello World

if, for, with, while

@for (var i in model) { Current: @i }
Code language: CSS (css)

And we also have support for helper methods, e.g.:

@helper writeAge(age) { <span>@age</span> } Hello @model.name, you are @writeAge(model.getAge()) year(s) old!
Code language: CSS (css)

Helper methods are transformed into internal functions of our template function. E.g, the above would be transformed into:

(function(model) { function writeAge(age) { var hr = []; hr.push("n "); hr.push(age); hr.push("n"); return hr.join(""); } // etc…. });
Code language: JavaScript (javascript)


This allows us to declaratively define template functions, just as Razor allows.

What’s missing
There is still quite a bit of work to do, bugs to iron out, one big glaring omission currently is support for else/else if statements, and the parser doesn’t really obey xml markup just yet, but for an initial release, I’m hoping its enough to get people interested. I’ll push it to github soon so you can start forking and hopefully get involved!

The script file is attached, remember, its an early work in progress! Let me know what you think 🙂

jazor-dev.0.1.0.js

Leave a Reply

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

Related Post