Simple Example of MVC3 with Spine

Github: MVC3 with Spine

It’s much harder keeping up with a blog than I originally thought (so many half written posts) but I just wanted to share with you a couple examples of interesting interactions between Server and Client MVC.

One of the pages we had recently worked on had a complex workflow for creating/updating user generated lists of ints, strings or datetime objects based on some business case. The lists required a little computation and a lot of user hand-holding so I opted for a single page, client side solution using a coffeescript MVC library (Spinejs) and client side templating via plain old jQuery tmpl (now depreciated) with minimal server interaction.

This something a lot of Ruby, Python and PHP developers have been doing for years but is fairly new to the .net community (me at least). I mean why must the server need to know about each item in the list as it’s created? Why not push ‘state’ up the stack and let the client be the source of truth (perform crud actions) while this page is active.

Client

On the client side I ended up with very standard, out of the box, spine code to create and save the list items.

Coffeescript..

class FooModel extends Spine.Model
  @configure 'Foo', 'value'

  constructor: () ->
    super

Rendering was a little more complex being as though I had to perform a small amount of computation, messaging and recalculating the current list based on prior inputs but once that was out of the way I could use my client side templates to render.

Client template in html..

<script id="foo-item-template" type="text/x-jquery-tmpl">
    <div class="foo-item" data-id="${id}">
      <input type="text" value="${value}"/>
      <input type="button" name="delete" value="delete" />
    </div>
</script>

Spine’s controller class handles the events and re-renders based on the current list of foo models using the template declared in the constructor.

Coffescript..

class FooController extends Spine.Controller
  events:
    'click :button[name="add"]' : 'addFoo'
    'click :button[name="delete"]' : 'deleteFoo'
    'click :button[name="save"]' : 'saveFoo'
    'change :input' : 'updateFoo'

  constructor: () ->
    super
    $("#foo-item-template").template "fooTemplate"
    
#...other code to do all of the things!

  renderTemplate: ->
    $('#foo-list').empty()
    sortedList = FooModel.all().sort FooCompare
    $('#foo-list').append $.tmpl("fooTemplate", sortedList)

The code I posted above is of course scaled down, removing all of the other events (saving, creating, deleting, etc) and calculations but you get the idea.

Server

A list can be one of three types (string, int, datetime) so they all require different messaging. I didn’t want to have 3 client templates sitting on my page with coffeecript sorting through values, picking a different template to render for each type so instead I used the magic of server partials.

By creating different partials on the server which house the different client side templates (and possibly their respective logic), we allow the server to still be in charge of how it’s rendered. While not terribly elegant it does get the job done for this example and you can imagine the possibilities with more complex systems.

View..

@model Areas.Foo.Models.CreaetOrUpdateViewModel

<div id="foo-wrapper">
    <input value="add" name="add" type="button"/>
    <input value="save" name="save" type="button"/>
    <div id="foo-list"></div>
</div>

@Html.Partial(Model.DisplayTemplate)

The partial now holds the html client template for the specific type.

_FooTemplate..


<script id="foo-item-template" type="text/x-jquery-tmpl">
    <div class="foo-item" data-id="${id}">
      <input type="text" value="${value}"/>
      <input type="button" name="delete" value="delete" />
    </div>
</script>


<script>
  function FooCompare(x, y) {
        return parseFloat(x.value.replace( /,/g , ''), 10) - parseFloat(y.value.replace( /,/g , ''), 10);
    }
</script>

The end result is

  1. a controller who only deals in complete state gets and saves
  2. javascript that performs a single task, agnostic of the type/templates it’s dealing with
  3. a simple view that allows the model to decide which client side template it will need

Really enjoying it so far, I’ll share more thoughts as I go.

Here is the github for the example project.

Things to note

Coffeescript has really shined on my current project. It’s ease of use, quick ramp up time and simple class/inheritance model makes it so that any of my developers can jump in and make an important bug fix in a relatively short amount of time.

Though I’ve ben very impressed with Spinejs, there are a few things I’d like to point out. Models are actually more like repositories which create a json objects representing your ‘model‘. It can feel awkward at first to use the same ‘class‘ (I know it’s not really a class) to talk about the repo and actual object but it functions well enough.

I’ve really enjoyed Spinejs’ events though. Having multiple controllers on one page interacting by firing events is written cleanly and simply, or at least as simply as javascript will allow it.

I wouldn’t recommend jQuery tmpl for rendering more than 200 complex templates at a time. We have a page currently which does exactly that but with a little imagination (magic, fun with javascript…) we were able to break up rendering asynchronously and re-render more intelligently. So far though it’s been very easy and a pleasure to work with.

-Jason Hooten

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s