Inside the ASP.NET Single Page Apps Template | John Papa

John Papa

Evangelist on the loose

Inside the ASP.NET Single Page Apps Template

...

The Single Page App (SPA) template is now available in the ASP.NET and Web Tools 2012.2 (Release Candidate). This SPA template has been updated since its beta release which I blogged about here. Kudos to Mads Kristensen for spearheading this at Microsoft. I’ve been a big fan of SPA and of Mads for a while. I was thrilled to see some of my feedback (from my previous post/review) make it into this template. This post contains a complete walk through of all of the key pieces on the server and client side.

What is the Intention of the Template?

Since the beta release of the SPA template, there are a ton of small revisions that I believe make a huge difference for the better. The intent of this template appears to be to provide a great starting place for building a basic SPA with 1 view. If you want to move on from here to provide other features such as multiple views and sharing of data across those views, you can do that by pulling in other libraries like Sammy.js or Breeze.js (or choose your own favorites). The point here is that the template is a SPA sample. If you are new to SPA, and many folks are, I recommend starting with this template as a sample. Then move on to build your own, using this as a starter model.

What has changed since beta?

The biggest change is in better Separation of Concerns (SoC) and Knockout intellisense (tooling). You can check out my previous post to see what was included in the beta version of this SPA template, here. Mostly its the same overall, however the biggest changes are subtle ones. These changes make it easier for you to start with this template and be able to move beyond it by adding more of “anything”. More views, more viewmodels, more libraries, more features. What are these subtle changes? Mostly they are in how the code is organized using modules. I’ll explain more below, but here is a high level list of what’s new:

Using the Template

Create a new MVC 4 Web Application and then choose the Single Page Application template.

The View

The code below comes form the index.cshtml view inside of the Views/Home folder of the sample app. Notice it starts off with a quick check to make sure the user is authenticated. If they are not, it shows a login page (not shown below), but if they are authenticated the page displays the available Todo lists. This is a simple way to add some authentication to your app using some built in ASP.NET features.

@if (@User.Identity.IsAuthenticated)
{

}

Inside of the if statement is all of the HTML and the bindings for the View. The sample below shows the HTML and Knockout bindings that begin to display all of the TodoLists. .

<button data-bind="click: addTodoList">Add Todo list</button>

<section id="lists" data-bind="foreach: todoLists, 
        visible: todoLists().length">
    <article class="todoList">
        <header>
            <form data-bind="validate: true">
                <input class="required" type="text" 
                    data-bind="value: Title,
                    selected: IsEditingListTitle, 
                    blurOnEnter: true" />
            </form>
       </header>
       <a class="deletelist" href="#" 
           data-bind="click: $parent.deleteTodoList">X</a>

       <!-- SHOW TODO Items for each list here -->

       <p class="error" data-bind="visible: ErrorMessage,
           text: ErrorMessage"></p>
    </article>
</section>

At the bottom of the View is where the JavaScript bundles are located. The bundles, created using ASP.NET’s optimization features, are only retrieved if the user is authenticated. Then Knockout is loaded, jquery validation, and finally all of the custom scripts. The custom scripts are your viewmodel, model, datacontext and custom binding handlers (in the todo bundle). It’s important that your scripts come last, since they likely rely on the other 3rd party scripts being loaded first.

@if (@User.Identity.IsAuthenticated)
{
    @section scripts {
        @Scripts.Render("~/bundles/knockout")
        @Scripts.Render("~/bundles/jqueryval")
        @Scripts.Render("~/bundles/todo")
    }
}

The Scripts

All JavaScript is contained within the Scripts folder. All custom scripts (ones you would write specific to your app) are contained within the app folder. I was glad to see this simple change to the template as I find it vastly easier to find my custom scripts without having to search through all of the scripts (including the 3rd party ones). It also makes it easier to bundle them (just grab that folder) or to automate static code analysis on that specific folder.

Notice that there are only 5 custom scripts in this app.

  • ajaxLogin.js manages auth
  • todo.bindings.js contains the custom Knockout binding handlers
  • todo.datacontext.js contains all remote data calls and data management
  • todo.model.js defines the client side models
  • todo.viewmodel.js is the glue between the View, Models and datacontext

Data Binding with Knockout

Notice some interesting data binding features, that use Knockout.js. You can create a list using Knockout’s click binding which calls the addTodoList method. This method binding invokes the addTodoList method on the JavaScript ViewModel.

<button data-bind="click: addTodoList">Add Todo list</button>

The ViewModel also exposes a todoLists property which will be iterated over using Knockout’s foreach binding. Notice the entire section is visible only if there are any todo’s (using Knockout’s  visible binding). It’s often a good idea to use the visible binding to hide and show DOM elements instead of adding and removing them. DOM manipulation is very expensive in browsers, while hiding and show is often much faster.

You could bind the visible binding to any expression that is truthy or falsey, like this: visible: todoLists().length

Once inside of the view, another foreach binding is used to iterate of each todo lists’s todos. Here the checked binding checks or unchecks a  checkbox, the value binding displays the values for INPUT type=”text” elements, the visible binding shows or hides an error message (for validation , and the click binding calls the $parent.deletTodo method. $parent is a feature in Knockout that let’s you go up one (or more) levels in the datacontext hierarchy. In this case the binding is to a single todo item. The deleteTodo  happens on the parent level (at the Todos list level), so to get there we have to reference $parent to get back up to that level first, and then delete it. You can learn more about $parent at the Knockout site or in my Knockout course at Pluralsight.

<ul data-bind="foreach: Todos">
    <li>
        <input type="checkbox" data-bind="checked: IsDone" /> 
        <input type="text" data-bind="value: Title,
            disable: IsDone, blurOnEnter: true" />
        <a href="#" data-bind="click: $parent.deleteTodo">X</a>
    </li>
</ul>

Custom Binding Handlers for Knockout

Also included are a few custom Knockout binding handlers, located in the /scripts/app/todo.bindings.js file. I like how these are organized together so you can easily pull them in when needed and possibly find them to reuse them in another project.

  • validate – invokes jQuery validation on an element
  • selected – selects (or unselects) a DOM element based on the bound property’s value
  • blurOnEnter – loses focus when the user clicks the ENTER key
  • placeHolder – a shim for the HTML5 placeholder for older browsers

The key here is not that actual binding handlers but rather that they show you a simple way to extend Knockout’s binding structure to add your own features. I do this in my Code Camper SPA course to create simple handlers like escape (which performs an action when the ESCAPE key is pressed) and more complex handlers for thing s like a custom starRating control.

JavaScript Models

The Models in the new SPA template (contained in todo.model.js)have a lot of great changes in them. In fact, the JavaScript is where most of the changes are. It starts with the Immediately Invoked Function Execution (IIFE) concept. IIFE wrap the entire model with a function and execute it immediately (thus the term IIFE). The code below shows the model is wrapped in function which accepts 2 parameters. These parameters are its outside dependencies: Knockout and the datacontext module (I’ll get to the datacontext shortly).

Inside of the IIFE are constructor functions that create the TodoItem and TodoList objects on the client (the models). The code below shows just the TodoItem for brevity, but the real code in the template also has the TodoList.

The TodoItem function is a constructor for an object that has properties for the Todo model (Title, IsDone, TodoItemId, etc) that are mapped from the DTO passed from the Web API. It also has some additional members that are not derived form the server nor the DTO such as a save method and a ErrorMessage property. The ErrorMessage property is an observable that can be set to display validation information about the object. The save method defers the save to a method in the datacontext module (more on that in a moment) which returns a promise.

(function (ko, datacontext) {

    datacontext.TodoItem = TodoItem;
    datacontext.TodoList = TodoList;

    function TodoItem(data) {
        var self = this;
        data = data || {};

        // Persisted properties
        self.TodoItemId = data.TodoItemId;
        self.Title = ko.observable(data.Title);
        self.IsDone = ko.observable(data.IsDone);
        self.TodoListId = data.TodoListId;

        // Non-persisted properties
        self.ErrorMessage = ko.observable();

        self.save = function () { 
            return datacontext.saveChangedTodoItem(self); };

        // Auto-save when these properties change
        self.IsDone.subscribe(self.save);
        self.Title.subscribe(self.save);
    };
})(ko, todoApp.datacontext);

This version of the model in the latest SPA template is cleaner the the previous one because it mostly handles the members of the model. It leaves things like saving and deleting to the datacontext module. In other words, the models just know about their data but not how to get or save their data. I say “mostly” because this model still has a save method, even though it defers it to the datacontext. What would I do? In my code I tend to move that logic to the datacontext. Models know about their properties and datacontext knows how to manage the data. In this simple 1 page and 1 view sample, it’s not a big deal. But when you get to larger apps, this separation can make a positive difference. I’ll be exploring that in my upcoming Pluralsight course on SPA fundamentals (date TBD).

Overall I like this form of the models much better than the previous version of the template. It practices better separation of concerns by keeping the models and viewmodels in different files and separates the logic between the viewmodel, models and datacontext much more cleanly. I addressed this in my previous post about the beta version of this SPA template and they’ve addressed my biggest concerns by doing this separation.

JavaScript ViewModels

The sample comes with a ViewModel named todoListViewModel (shown below). Think of it as “The View’s Model”. The View needs data. The models store the data. But the data is not in the format the view needs it in. So the ViewModel pulls the data from the models together and presents it in a way that the View requires. This is also known as the Model-View-ViewModel pattern (MVVM).
The ViewModel also adds additional features such as awareness of the the models, reference to the client side data services (todo.datacontext.js) for getting and saving data in the models, and validation/error information. It’s the presentation logic for the View.

window.todoApp.todoListViewModel = (function (ko, datacontext) {
    var todoLists = ko.observableArray(),
        error = ko.observable(),
        addTodoList = function () {
            var todoList = datacontext.createTodoList();
            todoList.IsEditingListTitle(true);
            datacontext.saveNewTodoList(todoList)
                .then(addSucceeded)
                .fail(addFailed);

            function addSucceeded() {
                showTodoList(todoList);
            }
            function addFailed() {
                error("Save of new TodoList failed");
            }
        },
        showTodoList = function (todoList) {
            todoLists.unshift(todoList); 
            // Insert new TodoList at the front
        },
        deleteTodoList = function (todoList) {
            todoLists.remove(todoList);
            datacontext.deleteTodoList(todoList)
                .fail(deleteFailed);

            function deleteFailed() {
                showTodoList(todoList); 
                // re-show the restored list
            }
        };

    datacontext.getTodoLists(todoLists, error); // load TodoLists

    return {
        todoLists: todoLists,
        error: error,
        addTodoList: addTodoList,
        deleteTodoList: deleteTodoList
    };

})(ko, todoApp.datacontext);

// Initiate the Knockout bindings
ko.applyBindings(window.todoApp.todoListViewModel);

What changed here? This module also now implements an IIFE that wraps the entire module. The module is executed and set to a variable todoApp.todoListViewModel.

In my post on the previous version of the template I pointed out that this template could benefit from using the Revealing Module Pattern. Well, in this new template the viewmodel uses the Revealing Module Pattern to help hide internal logic and expose the members that should be accessible externally. You can think of it as a way to hide private members and expose a public API. The public API is easily found by looking at lines 33-38 in the return statement. The todoListViewModel exposes an object with 4 members:

  • todoLists is an observableArray of todo lists
  • error shows error information relative to the viewmodel
  • addTodoList adds a new todo list
  • deleteTodoList deletes an existing todo list

So the big changes in the ViewModel are the IIFE, the Revealing Module Pattern, some camelCase cleanup on the names, and removal of the models (now in todo.model.js).

This file wraps up by binding the viewmodel to the current page using Knockout’s applyBindings method. In a small app this is fine, but in a larger app I like to consolidate all of my applyBindings method calls that link a View to a ViewModel in a single location (usually a bindings module). Why? Because then I can easily find all of my view to viewmodel bindings and I can streamline things like transitions and other common logic. (I do this in my SPA course at Pluralsight today.) But again, this is a perfectly fine way of doing it too.

I find the viewmodel to be very clean. I love how it does one thing well, and it doesn’t try to define models nor does it try to figure out how to load or save data.

DataContext

The todo.datacontext.js (formerly in the old template this was todoList.dataAccess.js) contains a module that manages all data http requests. When the viewmodel(s) need to perform actions on data, it can defer all of those to the datacontext (save, delete, fetch, etc). Both my course’s SPA and this sample follow this separation of data services, which I really like so it keeps the ajaxian plumbing out of my viewmodels.

Again, this module uses the IIFE and creates a module, this time named todoApp.datacontext. Notice is also uses the Revealing Module Pattern to expose just the members that are needed externally. This makes it simple to figure which method you need because methods like getTotoLists and saveNewTodoList are easily found and the internal logic is easily ignored.

window.todoApp.datacontext = (function (ko) {
    var datacontext = {
        getTodoLists: getTodoLists,
        createTodoItem: createTodoItem,
        createTodoList: createTodoList,
        saveNewTodoItem: saveNewTodoItem,
        saveNewTodoList: saveNewTodoList,
        saveChangedTodoItem: saveChangedTodoItem,
        saveChangedTodoList: saveChangedTodoList,
        deleteTodoItem: deleteTodoItem,
        deleteTodoList: deleteTodoList
    };

    return datacontext;

    // Removed additional internal logic for breviity. 
    // See full template for details

})(ko);

If you go back and look at the old template, I think you’ll agree that this version is considerably cleaner. Does it function the same, of course it does. But the big difference is that this code is easier to test, easier to read, and easier to expand upon.

Entity Framework DbContext

On the server they toss a very simple sample DbContext at you based on Entity Framework. You can obviously swap in and out whatever you want here.

public class TodoItemContext : DbContext
{
    public TodoItemContext()
        : base("name=DefaultConnection")
    {
    }

    public DbSet TodoItems { get; set; }
    public DbSet TodoLists { get; set; }
}

Server Side Models

The template comes with a series of models and Data Transfer Objects (DTO’s). It’s interesting that they decided to use both models and DTO’s which are intended to be vessels for the models’ data to send to the client.  This does offer more separation of models from DTO’s but I’m not sure I’d have gone this route with a template. I’d instead start with the models and not add the DTO’s til I actually needed one. (Or possibly I’d use a projection, but that’s a tale for another time.)

Web API Controllers

The template comes with a TodoListController and a TodoController.  They both have the [Authorize] attribute on the web api method calls, which is a good practice. The TodoListController has several methods for CRUD including a GetTodoLists (which gets all of the todo lists). It returns an IEnumerable (though with the new features it would be more interesting to return an IQueryable). Notice the code below retrieves the  Todo entities and then creates the Todo DTO’s from them, before returning them to the caller. Pretty straightforward. Of course, they also have the CUD method from CRUD (Create, Update and Delete).

The only big change here is that the return type is now an IEnumerable.

    [Authorize]
    public class TodoListController : ApiController
    {
        private TodoItemContext db = new TodoItemContext();

        // GET api/TodoList
        public IEnumerable GetTodoLists()
        {
            return db.TodoLists.Include("Todos")
                .Where(u =&gt; u.UserId == User.Identity.Name)
                .OrderByDescending(u =&gt; u.TodoListId)
                .AsEnumerable()
                .Select(todoList =&gt; new TodoListDto(todoList));
        }

The key takeaway here is how simple it is to set up your database, Entity Framework and API controllers to get you rock and rolling very quickly. It’s a very scaled down version of what I did in my Code Camper SPA and a great starting point. So what do you do with this part of the demo? I’d start by deciding how you want your Entity Framework code to look and if you want to use it as your repository and Unit of Work or you want your own Repo and UoW on top of EF (what I did in Code Camper). Both are good choices. Next I’d decide on your structure for your Controllers. I like Separation of Concerns (SoC) and recommend making your controllers responsible for pushing and pulling data. This means making sure you don’t add business logic to this layer. For that, I like the Controller to call a business manager layer who is responsible for handling all the heavy thinking. Finally, decide if you want or need custom DTO’s … I start without them and only add them if I see a need.

Should I Use This Template?

Yes, if you are new to SPA and are looking for a way to ease into it, this is a great choice. It gets you going quickly and provides a good foundation for you to build on. The key here is to not use this as law, but use it instead as guidelines from which you can deviate where it makes sense for you. Some deviations will be to use your own choice of libraries instead of what they (or I) prefer. Some choices will be to decide to use different separation concepts like how I prefer my models to not have a “Save” method. The goal of this template seems to be to give folks a place to start and provide them enough information to choose their own adventure beyond that. I believe it hits that mark and I recommend using this template for that purpose.

Choose Your Own Adventure

Once you get beyond a single page with a single view, you might want more features. For example, you want 1, 2, or more of these features:

  • Multiple Views
  • Page navigation between the Views
  • Change Tracking
  • Synching data between Views and ViewModels
  • Saving and restoring from Local Storage (tombstoning)
  • Caching data on the client
  • Filtering, sorting, paging, ordering on the client
  • Responsive design and UI concepts
  • Reverse ajax for server to client communications
  • Work with a static typed language like TypeScript

You can add in your own custom libraries or pull in 3rd party libraries such as Sammy for navigation, Breeze for rich data, SignalR for reverse ajax, or Amplify for local storage. There are a lot of directions you go from here and by no means are you done once you open this starter template which puts you on a road for your adventure. But that is the fun of a choose your own adventure.

How Do I Take This Further?

OK, so you want some of those features? You can start by looking at my existing end to end SPA Pluralsight course. Or if you want TypeScript you can keep an eye out later this month at Pluralsight for our TypeScript course (I am co-authoring with Dan Wahlin). I’ll also be producing a course for Pluralsight in the beginning of 2013 on how to get started with this SPA template, aimed at SPA beginners. I’ll walk through all of the pieces of the template, explain them, why they exist, how the work together, and discuss how to expand upon them.

tags: html5 javascript knockout mvvm SPA visual studio 2012 web api
  • Pingback: Knockout Intellisense in Visual Studio 2012 | John Papa

  • Nikita

    Thank you for this post

  • Sergey Shumov

    Great post! Very helpful for me, thank you!

  • Ray

    Thank you for your post John, btw I have a question.
    Instead of using razor code to check if user is authenticated or not, is it ok to use javascript code to check a user’s status via ajax request?
    For example:

    <!-- ko if: isAuthenticated -->
    html code for authenticated user
    <!-- /ko -->
    

    In viewmodel javascript, isAuthenticated property is set by ajax call.
    Is this way ok from security’s perspective?

    I want to get rid of any server side code in html view code so that the client code can integrate with any server side technology such as java, rails, asp.net and so on.
    Thanks in advance

    • John

      Ray,

      I recommend performing all security checks on the server. There are too many ways for a decent hacker to bypass security checks on the client in JavaScript. You could create an authentication service in a server platform and language of your choice.

      • Ray

        Thank you for your answer John,
        If I check that user’s role in the server side code as well, I mean both javascript code and server code check security stuff, is that ok to check user’s role in javascript code? I am little bit worried bad users can get security hint from the javascript code in this case as well.

        • http://robbihun.com Rob Bihun

          Ray,

          It would be fine to check the “IsAuthenticated” via a service call as long as you are okay with the fact that a malicious user can still see parts of the page you may not want them to see.

          For example, if you are hiding menu options unless they are authenticated and a malicious user, using the javascript console in their browser, set’s IsAuthenticated to True then they can see those menu options. If you are okay with that, and still doing an authorization check on the server side for things they can get to by manipulating the client side, I don’t see a problem with it.

          Now, if there is something on client side that they should NEVER EVER EVER see unless they are authenticated, stick to the server side razor syntax (or other framework) that way the markup never gets sent to the browser and there is no way for them to manipulate the page to see it.

          • Ray

            Thanks Rob, perfect answer !!

  • http://victorantos.com/resume.aspx knockoutJS

    Thanks!
    I’ve used SPA beta to create http://www.CanvasBM.com over the weekend and it worked beautifully, I’ve got now 1500+visitors in less than 2 months.

    Then I started a new SPA project which had multiple Views, Navigation, Grids etc. and I hit my head against the wall. But this new template makes me much more optimistic.

    PS: your “Are You Real?” answer box shows maximum one char, and hides the second one

  • http://w3portals.com Brad Oyler

    Awesome stuff. Anyone try using RavenDB with this? Could be a great fit. Plus, it also has a http API.

  • Pingback: MVC Single Page Application Template Update for ASP.NET and Web Tools 2012.2 RC - .NET Web Development and Tools Blog - Site Home - MSDN Blogs

  • Pingback: MVC Single Page Application Template Update for ASP.NET and Web Tools 2012.2 RC | MSDN Blogs

  • mravinale

    Would be nice to show how to use automapper in order to create a dto from the entity, instead of passing an object as parameter.

    • John

      Sure, you could use automapper. But remember, this is just a template they provide to get you started. There is a lot more you can do that goes way beyond this. If they added everything, then it would be very heavy.

  • Fredi Machado

    Awesome post John.
    I’m looking forward to see more of your Pluralsight courses.
    Thank you very much!

  • Daniel

    Hi,
    can you give me some recomendation on uploading images that should be transfered with EF POCO dto to controller and saved on file system or blob, whatever, after successfully storing EF POCO to db.

    By the way, great post John, look’s like our silverlight journey is finally,
    I don’t know, do I need to cry or be happy, over

    Thx

  • Pingback: Jakub Gutkowski | Jakub Gutkowski - Ksiązki na koniec świata (2012)

  • vichu

    Nice article. Its possible to get the code?.

    I like to use the knockout feature in SPA and not able to bind the knockout object to Partial view

    • John

      Vichu,

      You get this code when you install the update from Microsoft and create a new project.

  • vichu

    Thanks John. I willl try this. I will download Visual Stodio 2012 and SPA template from MSDN subscription and check.

  • samir benali

    great article, keep on evangelizing

  • Pingback: ASP.NET and Web Tools 2012.2 | Moretto Carlo

  • Pingback: Links of the week | Jan @ Development

  • Pingback: .Net News – Dezember Summary – Namics Weblog

  • http://www.dotnetnuke.com/Resources/Blogs/BlogID/244.aspx Ash Prasad

    Can’t wait for your simplified SPA video on pluralsight. The current one is great, but is too much info for someone to get started :)

    Any chance on beta video sooner :)

  • Pingback: Cheatsheet: 2012 12.17 ~ 12.31 - gOODiDEA.NET

  • Dave Van den Eynde

    Great stuff! Certainly I see a number of techniques that I’m using today, and seeing a number of techniques that I can start using today (without using the RC). What I do miss though is HATEOAS and the use of Knockout templates.

    I use HATEOAS whenever I can to avoid having to know what the URLs need to look like on the client. That means, for example, that in my representation of a list of Todos I would include, for each todo, only a minimal set of data needed to show in a list and a location of where the resource is of the individual Todo is located. The list and the individual item also wouldn’t be sharing the DTO (which I would name TodoResource). It’s a preference, and I’m sure I wouldn’t want to force this onto anyone who doesn’t like it.

    But the second thing that I miss is the use of client side templates to avoid having to look at a flash of unbound HTML before knockout kicks in and hides all the irrelevant stuff. Now, when your page is being loaded and the initialization script is about to be executed we’ll be looking at all the unbound HTML that flashes before our eyes. Especially when you have a larger SPA with multiple views you’ll be seeing a lot of HTML being loaded and especially on slower connections/mobile browsers this might be irritating.

    When the views themselves are put in a client side element, they’re not readily visible, nor even handled as DOM, until Knockout decides to put the relevant parts in DOM based on the view model.

  • http://www.tarcode.com Cindy

    Great post and really helps me a lot of the bar code project in web application. Thanks again.

  • http://icanmakethiswork.blogspot.com John Reilly

    Hi John,

    Thanks for this post; really interesting.

    I’m very keen on what I’ve seen on SPA’s but so far I’ve held off going fully down that route as I wasn’t sure whether I was happy about the implications for a very large application of having each “screen”s template loaded up when the app fires up for the first time. Also I wasn’t certain how to best facilitate individual edit screens within the context of a SPA. Inside some kind of modal dialog? If so which one? etc etc.

    I’ve just started working my way through both your SPA course on Pluralsight and I’m hopeful I’ll find answers to some of these questions as I watch this (the later module titles certainly sound like they’ll be addressing some of these topics).

    Before I sign off I wanted to say fine work on the TypeScript course – loved it!

    • John

      John R – Thanks, I’m glad you like the TypeScript course!

      View composition is an important topic and you can try t load on demand using templating libraries or require.js (the text plugin). But before you solve that problem, just make sure it is an issue. I agree in larger apps with more screens and things flying around that it makes sense. But before I tweak performance, I always test to make sure its an issue first. Otherwise, YAGNI :)

      I do recommend looking into Durandal.js. Its brand new and in pre-release, but I’m using it for my next course as it has a lot of upside and is built by the creators of Caliburn Micro.

      • http://icanmakethiswork.blogspot.com/ John Reilly

        Thanks John, YAGNI is always good advice :-)

        Durandal looks pretty interesting and potentially useful. Do you know what the name of the course you’re using it for on Pluralsight will be? That way I can keep a look out for it.

  • http://www.mhi.net76.net xtreamer

    awsome info..

  • http://jitendra.me Jitendra Singh

    SPA uses the best tech and tools which are in market. I am looking forward for another app with the changes in market.

  • Ollie Philpott

    “You could bind the visible binding to any expression that is truthy or falsey, like this: visible: todoLists().length”

    Shouldn’t this be “visible: todoLists().length > 0″?

%d bloggers like this: