Many modern websites use a single-page application (SPA) approach to provide a more fluent user experience, similar to a desktop or mobile application.
Unlike a traditional web application when for each request the web server renders a new HTML page; in a SPA, after the first page load all the further interactions with the web server happen on the client side through AJAX calls returning data for the partial page update, without a full reload. Data returning by AJAX calls is usually in JSON format, but simple HTML markup is also acceptable for partial renderings.
A diagram below illustrates the SPA approach in general:
The SPA approach gives us two obvious benefits:
In this article we will examine three approaches of a SPA implementation on Sitecore. The aim of our exploration is to provide an application-like user experience to the end user, while still maintaining support for the following features:
For the purpose of this article, we will be focusing on the back-end tactics without the detailed explanation of the front-end implementation.
1. Sitecore as a Content Database
Let's start from a simple option of using Sitecore as content storage only, without any presentation details such as layouts and renderings. The actual layout in this case will be be implemented by our front-end code.
We can choose any fancy new JavaScipt MVC/MVVM framework to create a web application - Angular, Ember, Backbone, you name it. Our application will call a REST web service in order to retrieve a Sitecore item in JSON format, for this we can either use the Sitecore Item Web API (included in Sitecore 7.1+) or create our own web API to pull data from Sitecore and then transform them, or use both.
This is a classic SPA approach where the resultant HTML does not contain the actual page content, but instead references to JavaScript & CSS. This is what prevents the page from being indexable by search engines.
The application life-cycle for such an implementation would be as follows:
This approach might be suitable if you already have a Sitecore powered website and you now want to create a new SPA that will reuse some of your existing Sitecore content.
For example, if you have an existing Sitecore website and would now like to deploy a new staff intranet. This new intranet is going to make use of a fair amount of your existing content from the main website and you don't care about SEO. In this scenario, it makes perfect sense to reuse the existing Sitecore items on your main website, and retrieve their content via the API.
This approach has the following advantages and disadvantages:
Pros
Cons
2. Server-side Rendering
In this approach, we'll give Sitecore more responsibility. Along with acting as a data storage, Sitecore will also control the presentation layer for pages, so the server will return back the full page HTML. To pull the page HTML and update its content in a browser accordingly, without the entire page reload, we will add a script that enables URL routing in the browser - this could be a custom module or some library, for example; PJAX.
so, the application life-cyce will be as follows:
On the server side we can identify AJAX requests and serve them by a lighter layout version to reduce the HTML size for transferring.
This approach has the following advantages and disadvantages:
Pros
Cons
3. Mustache Templates with JSON Page Model
From our experience the two approaches described above are still not ideal. We wanted to adopt a more elegant approach that would be SEO friendly, support all Sitecore features and still provide a clear separation between front-end and backend code responsibilities.
We found the best solution to be the use of Mustache templates for HTML renderings on both server and browser side.
The application life-cycle slightly differs in comparison with the second option and is as follows:
Below we will review the renderings in detail, starting from the server side:
We use the Controller renderings for all renderings. The controller code itself is almost trivial - it only uses Glass Mapper to map the Content Item fields to a strongly typed model and then returns the result:
CaseStudyController.cs
EMBEDDED CODE HERE IDK HOW TO ADD
The controller action returns a NustacheActionResult object - our custom ActionResult that renders a specific Mustache template using Nustache library:
NustcheActionResult.cs
EMBEDDED CODE HERE IDK HOW TO ADD
Here is a BaseController code inherited by all other controllers:
BaseController.cs
EMBEDDED CODE HERE IDK HOW TO ADD
Now it is time to look at the browser side and see what happens there. For example, a user clicked a link going to a case study page with URL '/case-study/1234', our custom URL router captures that click and makes an AJAX request to that URL, instead of navigating to it directly.
For this need we have extended "mvc.requestBegin" pipeline in Sitecore with an additional processor to catch all AJAX requests:
AbortSitecoreForAjaxRequests.cs
EMBEDDED CODE HERE UNSURE HOW TO ADD
This takes a Sitecore item and then maps its data and presentation details (layout and renderings) to a JSON page model, as per the following example:
Data mapping model
EMBEDDED CODE HERE UNSURE HOW TO ADD
Therefore:
In order to support Experience Editor mode we have a different set of Glass Mapper models where fields are wrapped into Editor controls. So, when you open a page for editing you get the same WYSIWYG editors as you do in the case of @Sitecore.Field() helper.
This approach has the following advantages and disadvantages:
Pros
Cons
We have employed the second and third approaches on our own projects. Although we found that the third option delivers the best results, it's important to note the need for full stack development knowledge. You need to have a very good understanding of Sitecore and be confident working in both backend (C#) and front-end (JavaScript) code. The second option is still a decent fallback method.