github linkedin rss
Server generated template response in Laravel
Aug 17, 2014

This post goes out to all of my fellow server-side renderers(?), assuming that you still exist.

Some of us have yet to transition to (only) client-side rendered applications. The cause for this might be either requirement or preference, it doesn’t really matter. Whatever the reason is, there are some cases where client-side rendering has its place.

While your application might be centered around server-side rendering, there is a possibility that some client-side rendering will make your application more seamless. In my case, I often want to create new resources at the same page that lists existing ones, and then see the new ones without having to refresh the page. To accomplish this I’ve often duplicated the resource template for both server and client.

To make the above more concrete, consider the following example. We have a list of users (resources) that we want to render, therefore we create a blade view - called users.blade.php.

<div id="users">
@foreach($users as $user)
  <div class="user">
    <h2>{{ $user->name }}</h2>
  </div>
@endforeach
</div>

At this point we decide that we want to be able to create new users via a form on the same page, and render them instantly after creation.

How do we accomplish this? 1. We need a form for user creation. 2. We need some JavaScript that handles the input from the form and sends a POST request to the server, and on sucessful creation renders the new user. 3. We need to know how a user is presented in order to render it. Therefore we need a template which we can use through JavaScript.

The form:

<input id="username-input" type="text">
<button id="new-user-btn">Create</button>

The template:

<script id="user-template>
<div class="user">
  <h2><%= name %></h2>
</div>
</script>

The JS-file:

$('#new-user-btn').click(function() {
  var name = $('#username-input').val();
  $.post('/user', { name: name }, function(data) {
    // underscore template
    var template = _.template( $('#user-template').html() );
    // data is json containing user name etc
    $('#users').append( template(data) );
  });
});

The updated users.blade.php file now looks like this:

<div id="users">
@foreach($users as $user)
  <div class="user">
  <h2>{{ $user->name }}</h2>
  </div>
@endforeach
</div>

<input id="username-input" type="text">
<button id="new-user-btn">Create</button>

<script id="user-template>
<div class="user">
<h2><%= name %></h2>
</div>
</script>

While this approach works, my main problem with it is duplication. If we ever want to change how a user is presented, we have to remember to change both the template on the server and on the client.

A solution

After some investigation, and looking into how others solve this problem, I stumbled upon a post by DHH called Server Generated JavaScript Responses. In the post DHH explains how they return HTML templates in server responses, which are then used to render the resource. With this post in mind I asked myself how to use a similar approach in Laravel.

So lets extract a part from users.blade.php into its own view, templates/user.blade.php.

<div class="user">
<h2>{{ $user->name }}</h2>
</div>

To make use of the new view in users.blade.php we alter it to look like:

<div id="users">
{{ View::renderEach('templates.user', $users, 'user') }}
</div>

And the controller / routes would look like this:

Route::get('/users', function() {
  $users = User::all();
  return View::make('users')->with('users', $users);
});

Route::post('/users', function() {
  $newUser = User::create(['name' => Input::get('name')]);
  return View::make('templates.user')->with('user', $newUser);
});

With this approach we return the HTML template as a new user is created, which we can then just append to the list of users.

Example of new JS-file:

$('#new-user-btn').click(function() {
  var name = $('#username-input').val();
  $.post('/user', { name: name }, function(data) {
    // data is a rendered html template
    $('#users').append( data );
  });
});

While this approach is neither groundbreaking nor a silver bullet which solves all of your problem it might be a helpful addition to your “toolbox”.


Back to posts