Everything began last week on a sunny monday, when Marco Pivetta (aka ocramius), my dear #zftalk friend, talk to me about client-side rendering (he’s building a CMS).
To be honest, I’ve never took time to investigate client-side rendering before, so I was quite lost. But after one week of research, I’m kinda feeling stupid. I feel like I just discovered a completely different way of thinking websites, and how to make them. I hope this article will make people realize this new trend, because it really does sounds exciting and you could be asked to use it rather sooner than later.
For those who know me, I’m a Zend Framework 2 contributor, so I may refer to Zend Framework, but of course it applies to any server-side technology.
Client-side rendering vs server-side rendering
There are chances like, that me, your workflow when thinking website was similar to this:
This is easy to understand:
- Client makes a request to your server.
- The framework dispatches the request to an action.
- Your action may load a lot of different objects (in this example I use a forum – because I’m making a Zend Framework 2 forum module (check it out and help us if you want !): categories, threads in the categories…
- Those data are sent to your server-side templates, renders it, and returns everything to the client.
As a consequence, as soon as the page is loaded, the user already has access to all information. And it’s SEO friendly too. Of course, when the user changes page, this round-trip to the server has to be made again, which slows done the user experience.
Like me, you have surely played with jQuery to make Ajax query. And, I suppose you understand me when I say that it is painful. Basically, you end up to bind a lot of elements and doing a boilerplate code that quickly becomes unreadable and unmaintenable. I won’t show you my JS code in my current website for your mental health…
Here is how it works with client-side rendering:
As you can see, this is not as intuitive. Here are the steps that happen:
- Client makes a request to the server.
- Most of the time, whatever the request (/forum/categories/1, /forum/threads/4…) the request will be dispatched to the same action, that will just returns a minimal HTML (most of the time, just the layout).
- The JS framework will make a request to a REST API (or any other endpoint, but REST plays really nice) to get the categories, and another one to get the threads (you could return both at once, but this may lead to a bad API design).
- The server will returns the data as JSON/XML.
As a consequence, instead of one single-request that would load everything, with client-side rendering you will have several small HTTP requests that are made to the server. Depending on the context, I suppose this may be slower.
BUT there are some advantages to client-side rendering:
- Less work to the server. It does not have anything else to do other than sending back JSON data. It does not have to take care of the rendering.
The hard parts
Complex conditional renderings
In one of my application, I have a part that is rendered according to “complex” criterias (if the user is logged, show this, if he is logged AND is the author, show this…). Such conditional renderings use several entities, and I don’t really know how to efficiently map this to client-side rendering and REST API, that basically returns one kind of resource.
When using server-side template and a framework like Zend Framework 2, internationalization is easy. You have some view helpers that will fetch translation, for instance, in a Gettext file. This means that it is really easy to extract the strings to translate from your website, send the file to translators, get back the file, update the translations…
Obviously, authentication is not as easy to do. On server-side, using Zend Framework 2 identity helper, you just can do something like this:
<?php if($this->identity() === null): ?>
<p>Not logged in</p>
<?php else: ?>
<p>Logged in as $this->identity()->getFirstName(); ?>
<?php endif; ?>
It’s not as easy in client side.
Some solutions to the above problems
One of the problem that annoyed me the most is that when you are going to an URL, let’s say /forum/categories/2, you’ll basically have an empty page without any content. This is sad because, after all, an initial request is made… just to return, at most, a stupid empty HTML layout. Why not use this request to load some data and bind it to the view ? It appears that I’m not the first one asking myself this but it’s not that easy to do (I didn’t manage to do this using AngularJS, while some other manage to do it natively).
The thing is that, to me, it appears that it’s a “everything or nothing”. You either render your template in server-side, or in client-side, but you can’t do both.
So… when to use client-side rendering ?
I’m asking myself this question. On the website I’m working, it’s really a “standard” website. Some pages have very complicated conditional rendering, with data coming from several tables in the database… I just fetch all the data I need in one action, render it and voilà ! I don’t really see how I could integrates this to my website. I’ve found some solutions, but they rely on complex solutions (for instance using another JS library like PhantomJS that would render the JS templates in the server).
One solution I’ve thought about is doing a “hybrid” approach. I didn’t try this approach yet, so give me feedbacks if you think it would work or not. My idea is to have two URLs. For instance, let’s go back with my categories forum example. I would have to different routes :
- /forum/api/categories/2 : this is a purely REST URL. It would be the API and would simply returns JSON data.
- /forum/categories/2/my-category : this is a “standard” action, dispatched using ZF 2 router, and with a more SEO-friendly URL. It would act as a “normal” action, so it would load not only the category 2, but also all the threads of this category, so that the server renders a completely constructed page.
Of course, if the user navigate to page 2, I would like NOT to reload the whole page, but being able to use my JS framework ! In AngularJS, I could use something like that
. I would attach an event handler to the /forum/categories/2/my-category?page=2 link so that when the user click it, it makes the Ajax request, and replace the actual content using, this time, an Angular template ! When the user goes to page 3, as this is already an Angular template, you have nothing to do.
The drawback of this approach is that you have to maintain 2 templates (and 2 routes, one for the API and one for the “first-time request”) : one template that would be rendered by the server (with <?php echo ; ?> code), and one using AngularJS binding mechanism. But on the other hand, I think that having a correct REST API is cool too, as you could use it for anything else (desktop application, native mobile application…).
Of course you have more work to do, but I think this approach would allow to have the best of both worlds, although I really hate duplicating the templates. But I’m not sure if this would work completely =)…
How to use Zend Framework 2 for client-side rendering applications ?
You will be using this a lot. And fortunately, it’s pretty good and intuitive. Instead of extending from AbstractActionController, your controllers will extend AbstractRestfulController. You will need to implement 5 abstract functions (getList, get, update, post and delete). Those functions will be automatically call by ZF 2 depending on the HTTP verb action (POST, GET, DELETE and PUT). If a parameter named “id” is matched, it will select get function (/forum/api/categories/2 for instance), otherwise it will select getList (/forum/api/categories for instance).
You will no longer use forms too (haha, quite ironic for me as I am one of the maintainer of the Zend\Form component ^^). In fact, as you will most likely render your form on client-side, you won’t be able to generate them using ZF 2 view helpers.
HOWEVER, you will still need to validate data ! In ZF 1 it was problematic because the validation rules were defined in the Form object itself. In ZF 2, this task was delegated to the InputFilter object. Therefore, you are encouraged to use the Zend\InputFilter component to validate/filter your data on your server-side. As a consequence, if you want to use client-side framework, instead of defining your input filter by implementing the InputFilterProviderInterface in your fieldsets:
class MyFieldset extends Fieldset implements InputFilterProviderInterface
public function getInputFilter()
'username' => array('required' => true)
Just define your input filter in a different class that extends Zend\InputFilter. This will allow you to create form but only to instantiate the InputFilter and use it in your REST actions (most likely update and create).
You’ll likely still use hydrators, most of the time in your REST actions to convert array data to an object, that will be consumed by your services.
Do you think client-side rendering should be only used for web-app (I mean, applications that look like desktop/mobile applications), or do you need that it could be used in standard website too ? Of course, it would make the user experience so much smoother, but there are a lot of things I don’t really figure how to make them properly using a client-side library.
My friend and me are still thinking about what to use for our ZF 2 Forum module, so please give me some feedbacks on this .