Single Page Laravel Application
Michael Calkins asked me on Twitter the following question:
I would love to see the processes and techniques you use when designing a SPA. UI/UX, API considerations, and those things.
I thought it was an interesting question and the answer could be a benefit to the community so I’m going to do my best to answer it by outlining how I setup a brand new single page Laravel application. My last two projects have been with BackBone but I’m not going to focus on the actual framework, more so, just the setup and techniques.
Directory Structure
First I setup my directory structure by putting all my files that need to be processed inside app/assets. Then sub folders for each major area such as javascript, styles (less/sass), vendor (bower or manually copied components), etc.
From here I used Grunt, Gulp, or whatever is the new hotness and setup all the processing tasks. This will output to the public directory and I recommend spending the time on setting up source maps from the beginning. This is something I wasn’t doing and it would be so nice to have now.
First Changes
I always use Blade templates and in my master layout.blade.php I set the following meta tags:
<meta name="env" content="{{ App::environment() }}">
<meta name="token" content="{{ Session::token() }}">
I use the environment as a flag for any features that should only be in production or for testing only in development. I typically use this for things like analytics tracking or in weird cases.
The token is used for CSRF when posting from your JavaScript. Here is an example app/javascripts/plugins/ajax.js:
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
var token;
if (!options.crossDomain) {
token = $('meta[name="token"]').attr('content');
if (token) {
return jqXHR.setRequestHeader('X-CSRF-Token', token);
}
}
});
As you can see this just pulls in the token and is always included on any ajax requests.
API Considerations
For both Snappy and Wardrobe I put all my controllers in the app/controllers/api folder and these are only used for javascript communication. I do recommend keeping these very thin so you can easily reuse code in the future when you need a mobile app. 🙂
A side benefit of putting them all in their own folder is the ability to utilize Laravel’s routing and request filters:
Request::is('api/*')
Route::when('api/*’)
Here is an example of one of my controller edit methods:
public function putEdit($id)
{
$response = $this->post->update(Auth::user(), Input::all());
return $this->handleJsonResponse($response, ‘Your post has been saved’);
}
Then handleJsonResponse() is defined in the BaseController and simply finds out the type of response and returns based on this:
protected function handleJsonResponse($response, $success = 'It worked!')
{
if ($response instanceof IlluminateSupportMessageBag)
{
return Response::json($response, 400);
}
return Response::json(['msg' => $success]);
}
This keeps everything really DRY and easy to maintain. Also by returning the validator it gets converted to JSON and is easily consumed in your JavaScript.
JavaScript
This area really depends on the framework you have chosen on how you set it up. I only have the following two tips:
- Pre-seed all your primary JSON.
- Abstract plugins
By pre-seeding all your primary models/collections this will save you on that initial load time of fetching all the records and getting the app up and running.
Plugins are a necessary evil. I recommend always wrapping them in your own code with a simple api that way it’s easier to replace out later.
In BackBone a common pattern is to use views for this and just instantiate it when you need it. Here is a good presentation that goes more in depth on this.
CSS
I’ve struggled writing good clean CSS. If I’m being completely honest a lot of times I end up with a mess on big projects. It starts off great but then a new feature here, a new feature there, and wham! a mess. 🙂
If I was starting a new app today I would follow an existing style guide such as the one GitHub published or Smacss.
Since I’m not a CSS wizard I do not want to steer anyone in the wrong direction. However, I do have one tip to share and that is use slightly obtrusive javascript. What this recommends is adding a js- class to all elements your JavaScript interacts with:
<a href=“#” class=“js-edit edit”>Edit</a>
On small apps this is over kill but if you plan on maintaining this for years to come this is very helpful for refactoring and noticing when selectors are used elsewhere.
UI/UX
I could be off base with this, but I feel the terms UI/UX are a little open for interpretation. If I ask 5 engineers to define it off the hip, they all would give me different answers.
To me this is the most important area of any application and I feel UI/UX is what drives emotion and leads people to an action. Most engineers are generally weak in this area. If thats you then your goal should be to get everything related to the design uniform and plan from the beginning for adjustments. This area is rarely static.
The design and usability is so important in any app. No matter if it’s open source or commercial this is where users spend 100% of their time. Users do not care about your code and all your fancy design patterns. They have a goal in mind and need the simplest and easiest way to achieve it. This is what makes or breaks a product. It’s also the hardest to get right.
The closer you are to the development of a feature the harder it is to notice little nuances. I’m no different. Anytime I complete a new feature I always ask for feedback before deploying. 9 times out of 10 the reviewer will spot something that I totally missed.
To reiterate, if you are week in this area get everything uniform and bring an expert in. If you don’t have the means of getting an expert then plan on spending lots of time thinking and getting feedback.
Is it really different?
I do not believe it’s that much different from building a “traditional app”. Instead of one you now have two, a backend for api endpoints and auth then the front-end or client for the end user.
Over a thousand words later I hope I’ve answered Michael’s question without opening up many more. 🙂 Nevertheless if you do have questions don’t hesitate to get in touch.