Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Single Page Application with MVC and SammyJS

0.00/5 (No votes)
27 May 2015 1  
How to develop a single page application (SPA) without a framework using MVC and SammyJS

Introduction

Traditional web applications reside mostly on the server, with heavy traffic flow between browser and server using GETs, POSTs (and in webforms, the dreaded postback) being the order of the day. More and more web applications are using a new paradigm, the Single Page Application or "SPA".

Common examples of SPAs are Facebook and Gmail. The concept of an SPA is to keep provide a better, more responsive, seamless experience for the user, so that they seldom have to leave the browser and therefore avoid long tedious full page refresh round-trips to the server. Frameworks like Angular provide extremely robust methods for creating SPAs, but when you don't have the time for the learning curve, or want something that's light-weight and works simply, a very focused router like SammyJS is an extremely useful alternative. This article introduces the benefits of using an SPA router and explains how to use SammyJS to quickly put together a clean, scalable, modular single page application with MVC.

Image 1

Why use a router

Let's consider a web based address book. It would have very basic functionality:

  • List addresses
  • Search address function
  • Create/Edit/Delete address entries

Even that restricted functionality however carries a reasonable data entry and management overhead.

Image 2

 

A common approach to workflow in a Single Page Application is to use JavaScript button clicks to move things along. This methodology encourages behaviour for example like using global variables to track the ID or state of a selected/current data record. While using this approach works to a degree, it can get messy very quickly. If we can solve this work-flow problem, we have the basis for clear separation of concerns which allows for a far cleaner, scalable and more maintainable solution.


Image 3

SammyJS is described as "Restful, evented Javascript". It allows us to work in an MVC manner, but on the client. Just as we have controllers in MVC and Web-Api that can accept verbs of get/post/delete etc, we can also implement these patterns using SammyJS.
In a traditional web-server we access the controller using full routes, SammyJS routing however uses ANCHOR TAGS "#" to hook routing together - you can see this in the example below that uses a standard "GET" verb to request data from the "#home" route.

JavaScript
this.get('#home', function (){
   // do something here and send data back to the caller
})

Using the code - setup

To get SammyJS up and running, we need to initialise it, and add some routes.  

JavaScript
//initialize "SAMMY"
var app = $.sammy(function () {

    this.get('#home', function () {
        // show home screen
    });

    this.get('#address/create', function () {
        // show create new address screen
    });

}).run('#home');//run it with default html fragment


In the above code, we initialise the library, and activate it at the same time using the "run" method, giving a default route of "#home". We now have two routes, one that serves the home page to the user and another that shows a create new address screen. Both are using simple GET verbs. The objective of this article is to get you up and running quickly with SammyJS as a routing solution to use in your MVC apps so I will now run through common use-case scenarios and how they might work - we will avoid building a full application, however trivial.

Using the code - routes and verbs

For demo purposes, I create a number of DIVs that will act as display forms/regions that are shown/hidden depending on the route choice the user makes. These can be separated out into individual cshtml files and rendered using HTML.Partial(..)

HTML
<div class="divPanel" id="div1" style="display:none">This is div 1<br />
<label id="div1_SomeLabel"></label></div>

<div class="divPanel" id="div2" style="display:none">This is div 2, the param was: <label id="div2_ParamVal"></label></div>

<div class="divPanel" id="div3" style="display:none">This is div 3</div>

<div class="divPanel" id="divHome">This is the Home div<br />

<p><!-- master div that will be used to host other divs --></p>

<div class="divPanel" id="divContent"> </div>

Of course the routes have to be called from somewhere - I have a basic menu structure that demonstrates different use cases

HTML
<a href="#home">Home</a> |
<a href="#d1">Show Div 1</a> |
<a href="#d2/id=1234">Show Div with param</a> |
<a href="#d4">Re-direct to div 1</a> |
<a href="#GetRemote">Get div content from server</a>

There is also have a simple html form that will be used to demonstrate posting form data

HTML
<form id="simpleForm" action="#/post/form" method="POST">
    <label>Some Name</label><br />
    <input type="text" name="some_name" value="a simple name" />
    <input type="submit" value="Submit" />
</form>
The first two example routes are very basic, they hide/show different DIVs to the user depending on the route instruction.
JavaScript
this.get('#home', function () {
    hideAll();
    $('#divHome').fadeIn();
});

this.get('#d1', function () {
    hideAll();
    $('#div1').fadeIn();
    $('#div1_SomeLabel').text('');
});
Sometimes we will want to send the user from one route to another - this is where the re-direct method comes in
JavaScript
this.get('#d4', function () {
            hideAll();
            this.redirect('#d1');
            $('#div1_SomeLabel').text('Redirect from some a-tag');
        });
The next example shows how to get a value form a query-string that is sent in - first, here is the HTML again that calls the route
HTML
<a href="#d2/id=1234">Show Div with param</a>
Note the parameter "id", and the value "1234". We access the query name and value in a route using "params". Note the value place-holder ":" in the route pattern, and the "params" value being accessed.
JavaScript
this.get('#d2/:id', function () {
    hideAll();
    var tempID = this.params['id'];
    $('#div2').fadeIn();
    $('#div2_ParamVal').text(tempID)
});
Working with forms operates in a similar manner. Declare the form (note the method and the action route)
HTML
<form id="simpleForm" action="#/post/form" method="POST">
    <label>Some Name</label><br />
    <input type="text" name="some_name" value="a simple name" />
    <input type="submit" value="Submit" />
</form>
With SammyJS, we then extract the values that are sent in the form. (A small gotcha here is you need to stop the default post back to the server by the form by returning "false")
JavaScript
this.post('#/post/form', function () {
    alert('here with ' + this.params['some_name'] + '\n\nNow hiding the form!');
    $('#simpleForm').hide();
    return false; // stops the default POST behaviour back to server
});

Beyond the router

Creating a Single Page Application has many challenges, including memory management, data exchange with server, etc. Here is a small example of taking a fresh, updated server-side partial page and using it to update the interface for the user.
HTML
<!-- master div that will be used to host other divs -->
<div id="divContent" class="divPanel"></div>
JavaScript
// Sammy routing
 this.get('#GetRemote', function () {
            hideAll();
            GetRemoteDiv();
        });

// Ajax call to get remote html to replace page content
    var GetRemoteDiv = function () {
        $.ajax({
            method: 'get',
            url: '/home/LoadDivFromRemote'
            }
           ).done(function (dataRcvd) {
               $('#divContent').empty();
               $('#divContent').show();
               $('#divContent').html(dataRcvd);
           });
    }
C#
// server-side C# code that generates the dynamic page data

 public ActionResult LoadDivFromRemote()
        {
            ViewBag.Title = "Remotly injected!";
            ViewBag.Message = "This div was remotly injected into the dom from the server at: " + DateTime.Now.ToLongTimeString();
            return PartialView("~/Views/Home/RemoteDiv.cshtml");
        }
HTML
<h2>@ViewBag.Title</h2>
<h3>@ViewBag.Message</h3>

Wrap-up

This article has focused on the core concept of routing - what you do with routing is another thing - I suggest you work through the "JSON Store" examples (part 1, part 2) on the SammyJS website to get an in-depth understanding of the capabilities of this library

SammyJS comes with a raft of extremly useful plugins that cover topics such as html templating, client-side local storage, spa google analytics helper, etc.

I strongly encourage you to download the sample code and try SammyJS out

Other resources

For a more in-depth walk-through of the architecture of an SPA using microsoft technologies, read Mike Wassons article on msdn. Here are two other general resources on Single Page Applications that go quite in-depth

Finally, if this article was useful to you, please don't forget to give it a vote at the top of the page !

History

27/05/2015 - Version 1

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here