uniting the client and the server side... sort of
In this post, I'm sharing some code I've been working on to try and help address an issue I think is prone to happen with many web applications: keeping server side and client side rendering somewhat consistent.
Web users have grown accustomed to rich web experiences with plenty of nice interactions without page refreshes. Search engines, on the other hand, continue to require that data be rendered directly in the browser (crawlable AJAX is orthogonal to this detail) if you want to have any SEO relevance. You end up left with a dilemma: you have to render some of your data server side to keep people coming to your site, and you need to be able to render additional data client side to keep people coming to your site. While these are easily achievable using loads of technologies, keeping them consistent is slightly more challenging.
handlebars and helper functions
Disclaimer: I'm a handlebars fan boy, and an equally big fan boy of Edgar Espina's handlebars-java, a server-side implementation of the templating engine. I think handlebars adds just enough to mustache to get around some of the violations of DRY and provides better modularity of the view overall.
The biggest, most versatile tool in handlebars' shed is, in my opinion, helper functions. They provide a way to modularize view logic in a nice reusable way. A good example of this is alternating classes when rendering a list of items.
Helper functions get even more powerful in handlebars-java, where you can implement them in Java on the server side. Need to fetch data from the database? Knock yourself out! Need to make a web service call? Go for it! You can also use helper functions written in JavaScript on the server side as well (Edgar was nice enough to implement this after I made a request), so you don't have to worry about your Front End Developers having their code left out.
compilation and precompilation via handlebars-java
This probably sounds confusing at first blush; how could there be compilation and precompilation? Typically for the client side, you'd want to precompile your templates; asking your clients to do the compilation for you is, well, a bit rude. This turns any handlebars template into a JavaScript function that will perform all the rendering you set up in your template. JSON objects are passed to the function to resolve the values referenced with the {{}}
syntax.
Everything in handlebars-java is compiled via an ANTLR grammar, and all object graphs passed into it when rendering have their values resolved via reflection; no serialization into JSON takes place. Precompilation within handlebars-java does the same thing that precompilation with handlebars.js would do, except that it can render the precompiled template inline on the page for you on the server side. This may not sound like anything to write home about, but it provides several advantages:
- Your server can automatically recompile and re-precompile your templates as you make changes; effectively automating the process.
- The caching mechanisms in handlebars-java are well designed, so you don't end up unnecessarily recompiling anything.
- Upgrading handlebars.js can be automated since all templates will be re-precompiled (though this could also be undesirable since it could involve major regression testing).
- Anything compiled server side for server side rendering can be compiled into JavaScript for client side rendering.
Ultimately, you can reuse a good portion of your server side stuff on the client side as well. An example of this is presented further down.
bringing jersey into the mix and mapping helper functions to resources
If we're going to have all sorts of nice client side functionality, pulling down data after the page is rendered, we'll need some resources on the server side to handle it. At the same time though, if we're pulling in data via helper functions, it would be advantageous to keep the code execution paths the same. My proposal to you is that a Java handlebars helper function could shadow a Jersey resource if it's something that is expected to be available client side as well. My suggestion requires some amount of dogmatic conventions that may not be suitable for all audiences:
- Your
@Path
annotation and the name of the helper method should be the same. - The argument to your resource's GET should be the same as the argument to your handlebars function's
context
argument. - The helper method and the resource should exist in the same class.
I think this makes more sense with an example. Let's say we have a site that allows people to post messages, and other people can subscribe to the messages they post. Since no large website has cornered the market on this yet, I figured it would provide a fun "what if" example.
Here's an example of what I would have written as a resource for messages:
using handlebars templates with our... err... "functional resource"
Let's take a look at what a Handlebars template rendered serverside might look like:
As you can see, we're using the server-side helper function within the {{#latestmessages}}
tag, and passing the data to a partial called messages
that looks like this:
Now, ironically, the mobile example I have is larger than the desktop one because of the JavaScript involved with making the calls. Chances are that you would be dealing with a lot more data than my example shows under real conditions, so I'm asking for your forgiveness on the silliness of this disparity. That said, the mobile version:
Allow me to call out the important bits of the page:
- We're pulling in the handlebars JavaScript (full for the example rather than runtime) and jQuery. It's worth mentioning templates rendered server side don't have to pull in the handlebars JavaScript.
- Inside of a
script
tag, we're calling theprecompile
function that exists server side, and precompiling ourmessages
partial into the page as a JavaScript function. - On the server side, we're rendering a series of names we've looked up, and a link on each name. (Note: we're not loading any messages server side)
- We're binding a function to click events on each name link that will hide other people's messages, and will then either fetch the selected person's messages or display messages that were already fetched for that person.
- When fetching messages, we're calling
latestmessages
as a URI and appending thename
to the URI, the same way we called{{#latestmessages name}}
on the server side. This is the consistency that I've been trying to drive towards.
automatically mapping "functional" resources to their helper functions
I'm really not thrilled at the notion of having resources representing functions; but in this case the direction I'm trying to go is having a function really just provide a means of fetching data and nothing else. Ultimately any Jersey resource maps an HTTP method to a Java method in the resource class, so in my mind they're not so far removed with the exception that handlebars effectively maintains all helper functions at a single level.
That aside, per another blog post I wrote about resource filters in Jersey, I'm going to create a filter that will register any resource that is also a helper function within handlebars-java and use the value in @Path
as the name. In its current form I feel like this is a bit crude, though it is effective if you follow the convention:
By doing this, you're creating a one-to-one relationship of a helper function to a resource for consistency purposes.
an example project to mess around with
I set up a project on Github that you can use to mess around with this pattern of doing things. It's by no means perfect, and I plan on playing around with it more, but I think it can offer a fair amount of flexibility if you're willing to buy into the pattern as a whole.
Please feel free to share your thoughts on this approach; I'm interested to hear your feedback!
Could you elaborate more on server-side Handlebars *rendering*? I understand server-side compilation, or "precompilation", but I don't see the HTML actually being fully rendered on the server like traditional templating languages.
ReplyDeleteThanks,
Jordan
Sure; I think this is one of those cases where I already had all this information in my head and unfortunately left out some critical details in the post.
DeleteI'd encourage you to take a look at the Github project I linked to for an example of rendering HTML on the server side; specifically these files: https://github.com/theotherian/handlebars-client-and-server-side/blob/master/src/main/java/com/theotherian/handlebars/DesktopView.java and https://github.com/theotherian/handlebars-client-and-server-side/blob/master/src/main/resources/home.hbs.
Whenever you get back an instance of Template from a Handlebars instance, you can call template.apply(some object) to get the rendered HTML. The object you pass in should be an object graph that matches the names of the properties you're referencing in your template.
Hopefully the example of the resource and template above disambiguates that a bit for you :)
I will share it with my other friends as the information is really very useful. Keep sharing your excellent work.free Tamil chatting mobile site
ReplyDeletebreville 830 portafilter
ReplyDeleteThe use of coffee portafilter
Although the coffee spoon looks like an ordinary spoon, it can not be used randomly. Usually, it is mainly used to stir coffee, so that the cream and sugar of coffee can be better dissolved. When not in use, it should be placed next to the coffee saucer in a regular manner. The coffee is hot when it is first served, but you can also gently stir it with a coffee spoon to help reduce the temperature and make it a better entrance.