Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to serve pre-rendered data-bindable templates

There are a lot of data-binding client-side frameworks these days (Ember, Backbone, Knockout and Angular are the popular ones I'm aware of).

The problem with all these frameworks is the initial load. You have to mark your HTML up with placeholders, and depending on how it's done, the client will either see empty page chunks, or random {{ markup }}. Further, most of these templates are parsed client-side, rather than pre-compiled.

Ideally, when the user first comes to the site, they would be served some pre-rendered HTML with all the data filled in, and that would give them something to look at while all the pre-compiled JavaScript templates load and the data-bindings kick-in. From there on out, it would use client-side routing and we would just have JSON on the wire.

I can do half of this already with Jade or a handful of other template languages that can be rendered server or client-side. The thing I can't figure out is how I would get this to play nicely with one of those frameworks.

For example, in Knockout, your view might look like this:

<p>First name: <strong data-bind="text: firstName"></strong></p>
<p>Last name: <strong data-bind="text: lastName"></strong></p>

In Jade, I would define the same template like this:

p First Name 
  b= firstName
p Last Name 
  b= lastName

Which, for the curious, gets compiled to this:

function anonymous(locals) {
    var buf = [];
    var locals_ = locals || {}, firstName = locals_.firstName, lastName = locals_.lastName;
    buf.push("<p>First Name <b>" + jade.escape(null == (jade.interp = firstName) ? "" : jade.interp) + "</b></p><p>Last Name <b>" + jade.escape(null == (jade.interp = lastName) ? "" : jade.interp) + "</b></p>");
    return buf.join("");
}

Which is super quick to render either server- or client-side.

To get the data-binding to work, however, I would have to doubly annotate the source:

p(data-bind='text: firstName') First Name 
  b= firstName
p(data-bind='text: lastName') Last Name 
  b= lastName

i.e., put each placeholder variable in there twice, in two different formats.

(Fortunately, most of these frameworks take some sort of JSON object for the View Model (I believe), which I could serve directly to Jade or the framework, so that part shouldn't be an issue.)

I would very much like to avoid that but I can't think of how this might be possible without writing my own templating language which has a strong dependency on the frontend framework.

Is there a way to do this without a lot of pain? Using any of the above frontends or any JS-compilable templating language? Perhaps via some plugins/extensions rather than having to redefine it at a core level?

like image 901
mpen Avatar asked Dec 14 '13 06:12

mpen


3 Answers

You might want to try angular, with node:

https://www.npmjs.com/package/ng-node-compile

like image 58
MoLow Avatar answered Dec 06 '22 20:12

MoLow


Facebook's ReactJS implements a virtual DOM that lets you render the entire UI server-side (in Node).

like image 38
mpen Avatar answered Dec 06 '22 21:12

mpen


Angular 4+ has now Angular Universal, which allows SPA developed with Angular 2+ to be rendered in client side or in server side (with NodeJs or even Asp.Net Core). The tricky thing is that ANYTHING that relies on window, sessionStore, localStore, etc. won't be executed in server side, because there is no browser in server side environment. So keep it in mind if you develop a SPA and are planning on rendering at server side to become SEO friendly.

React and VueJs are also frameworks/libraries that can be used for universal applications (to be rendered at server side and serve already rendered content to the browser)

like image 25
diegosasw Avatar answered Dec 06 '22 21:12

diegosasw