Creating mocks with Breeze.js

Breeze.js is a powerful library you can leverage in your Single Page Applications.  It plays nicely with all other technologies, and is designed to provide rich client-side data.  You can easily it with your application, regardless of the tech stack.  Don’t believe me?  Prove otherwise.  The authors of Breeze.js have certainly shown you can in their samples – http://www.breezejs.com/samples – (Side note: check out the ESPN sample, whoever made it is a genius)

Mocks –

Most applications have need for mocks.  Mocks can be especially powerful when you are building out the front-end and the back-end isn’t entirely ready yet.  You can also use them for writing unit tests against data without having to populate from the server.  I won’t get into testing in this post because there are so many ways to test and the tests can be very opinionated.

The only requirement for mocks is that you need to have your metadata in place first, either from the server or as the Breeze.js authors mention in the docs you can also get a snapshot of your metadata and pass it in when mocking. This is a great way to iterate and update your models. (Reference – http://www.breezejs.com/documentation/load-metadata-script)

Example –

function loadMocks (manager) {
    var personMockOne = manager.createEntity('Person', { id: 1, firstName: 'John', lastName: 'Smith' });
    var companyMockOne = manager.createEntity('Company', { id: 1, name: 'Acme Inc.' });
    companyMockOne.employees.push(personMockOne);
}

That’s it! We now have mocks we can run our tests against or build out our pages. Calling loadMocks() and passing in our manager will add the mocks to our entity collection and we can start testing. We pass in the manager to keep our function anonymous, and in the above example I am adding the employee mock to the company’s employees collection, but we could just as easily create the company and then set the company property on the person to companyMockOne. Example –

var companyMockOne = manager.createEntity('Company', { id: 1, name: 'Acme Inc.' });
var personMockOne = manager.createEntity('Person', { id: 1, firstName: 'John', lastName: 'Smith', company: companyMockOne });

You will notice when using this technique that the entityState of the entities are added. If you have logic in your page related to saving or cancelling changes, this could potentially trigger your UI to think the mocks need to be saved. We can easily get around that as well by calling acceptChanges() on the entityAspect of the entity. Example –

var companyMockOne = manager.createEntity('Company', { id: 1, name: 'Acme Inc.' });
companyMockOne.entityAspect.acceptChanges();

This will let Breeze know that you are comfortable with the changes made to the company and they do not need to be persisted to back to the database. While you could also chain the entityAspect.acceptChanges() onto the end of the createEntity method, beware that this will no longer return the entity.

var companyMockOne = manager.createEntity('Company', { id: 1, name: 'Acme Inc.' }).entityAspect.acceptChanges();
// The next line will not console log our entity
console.log(companyMockOne);

I often use this method for create enumerations as well that aren’t strictly passed down from a server. This can be especially useful when using someone else’s API where they aren’t exposing all of the data you need. Last Example –

manager.createEntity('Priority', { id: 1, name: 'Severe', imageSource: 'severe.jpg' }).entityAspect.acceptChanges();

This can be especially useful if you are re-using the enum all over your application.

I hope you find this useful, please leave any feedback below, especially if you see any errors. Direct any trouble-shooting questions to StackOverflow.com, and check out the Breeze.js UserVoice for feedback or feature requests.

Advertisements

Best parts about Durandal

I wanted to start a quick series dedicated to what makes Durandal so powerful to yield.

Part 1 – 

The Compose binding

With Durandal, you can dynamically load views and AMD modules directly from the DOM.  Why is this important?

  1. You can separate your views and modules into the smallest bits of (re-usable) code needed.
  2. Render your content more dynamically.
  3. You can easily instantiate and show either Singleton objects or Constructors.  (wait what?)

Separating your code

Imagine you want to create ‘n’ number of widgets that are in charge of their own visibility, data-fetching, and data-binding.  Easy.  Using the compose binding with a foreach, you can instantiate the modules in your view model and use the DOM to render them.

View –

<ul data-bind="foreach: widgets">
     <li data-bind="compose: widgetPath"></li>
</ul>

View Model –

define([], function () {
    var widgets = ko.observableArray();
    function initializeViewModel() {
        widgets.push(new Widget('Compose a widget module', 'viewmodels/exampleone'));
        widgets.push(new Widget('Compose a widget view only', 'exampletwo'));
    }
    function activate() {
        initializeViewModel();
    }
    function Widget(title, path) {
        var self = this;
        self.Title = title;
        self.widgetPath = path;
    }
});

What did we just do?

1. When we initiated the view model, we added two widgets into an observable array containing widgets.
2. The first widget is a path to a view model, which Durandal finds an associated view for and properly recognizes as part of a module.
3. The second widget is a path to a view, with no corresponding view model.

Whats interesting to note here is that in the first widget with a corresponding view model, Durandal helps us by creating a context around that view that is bound to the view model. This means if we reference something in the view model that Knockout and Durandal bind any related view elements to that instance of the view model. This is most ideal when not using singletons, as the every instance of a singleton will be bound to the same context.

In the second widget we are simply binding to the current context, which in this case would be the widget.  This is derived from the foreach binding that the widget is nested inside of, so you could easily bind to the title property or you could also reference the parent context using the Knockout $parent reference.

The reason $parent would not work in the first example is that the new context does not directly know who the parent is.  What if we wanted it to?  That’s easy as well, using activationData

Activation Data –

Using the activationData option of the compose binding, we can inject data or even context into the composed view model.  This is an excellent way of injecting dependency, and I could go on for hours on the various ways you can use this in your application, but let’s keep it simple and show an example.

Let’s bind our view up to widgets using Knockout’s containerless binding –

<!-- ko foreach: widgets -->
    <!-- ko compose: { model: widgetPath, activationData: { data: $data } } -->
    <!-- /ko -->
<!-- /ko -->

You’ll notice that I passing an object into the compose binding that contains a model and activationData, which contains an object of it’s own. You can read more on the Durandal website on the various parameters (http://durandaljs.com/documentation/Using-Composition.html) but basically the model is declaring that at that path, there is a view model to use for binding. Durandal automatically finds the corresponding view, and then injects our activationData into the view model. To intercept it, you only need to have a parameter in your activate method of the widget. Notice I am injecting $data, which basically just injects the current context into the view model. You could just as easily inject strings, observables, and even parent contexts.

Grabbing the data in our widget’s view model –

define([], function () {
    var thisContext = ko.observable();
    function activate(activationData) {
        // Set thisContext equal to the injected data
        thisContext(activationData.data);    
    }
    function Widget(title, path) {
        var self = this;
        self.Title = title;
        self.widgetPath = path;
    }
});

Now you can bind thisContext in the view however you want. A solid strategy is to use the with binding in your view that is set to thisContext, that way if for some reason your view model is instantiated without data it will just appear empty as opposed to breaking your bindings.

That’s it! Feel free to leave comments if you notice any problems or have any suggestions or questions. If you have any Durandal questions please direct them to StackOverflow.com or the Durandal.js Google Group, where the active community will help resolve them.

Knockout.js – why it’s not a framework

Many questions come up in reference to Knockout.js, and most of the time it is a developer who is new to the use of a JavaScript library such as Knockout.js who is coming from some other language and looking for what are the key tools to use.  Knockout provides developers with an easy to use two way data-binding paradigm that is rivaled by none (in my humble opinion)

Why is Knockout.js so hard to understand?  Meno’s idea of ‘The Learners Paradox’ references why this is so difficult in a new perfect way.  When Socrate’s asks Meno’s to find answers to something of which he does not yet understand, Meno’s replies that how could he seek out knowledge without the ability to know what to ask for.  

This perfect example illustrates why the Q&A style never works in computer programming.  The world of programming and computer technology is ever changing and as such it takes one more skilled than I to understand how to ask a question that has an answer for which you do not yet know of.  If you knew what the answer consisted of, and therefore knew the question to ask, why would you ask the question of others as opposed to the search engine of your choice?

The answer is that you wouldn’t.  As a self-respecting nerd, or anyone of any background, you would never ask a question of others unless you either knew the answer already and were seeking approval of your understanding of the answer, or you were too tired or lazy to look up the answer in the docs or whatever else was available at the time to you.

Knockout has the best doc’s of any JavaScript library available.  Period.  No other library or ‘framework’ keeps it as simple as Knockout.  Want to know the definitive answer?  Google it…  Boom, there it is.  Take Angular as an opposing example – When you google what you may be doing wrong in whatever portion of the code, you may get hundreds of results on how the author did it, as well as the Angular docs themselves which aren’t very definitive at all.  The Angular docs give examples of how it could be done, following the authors exact use case, and then go on into why each commenter disagrees with the OP or why the OP’s answer is wrong.  

Knockout’s area of concern is strictly related to two-way data-binding, and nothing else.  Want templating?  It’s there, but not great.  It’s there to provide the easiest way to perform the action that you intend to use without forcing you to use a specific methodology.  Want a data-library?  Then look no further than the plethora of libraries that provide such functionality.

Knockout.js is not an end-all framework or library that forces you to choose a path.  Knockout makes such an obvious choice for developers because it generally points you in the right direction.  Want to map simple objects from the server?  Use the mapping plugin.  Want to create form validation on the client?  Validation plugin…  Want to figure out how to create expert-level custom binding handlers?  Turn to StackOverflow.com, and users such as http://stackoverflow.com/users/594420/rp-niemeyer and http://stackoverflow.com/users/1287183/michael-best

 

Consuming an API is a Breeze

Having trouble getting your new JavaScript libraries to play together nicely?  Want to see how you can use Breeze.js and Knockout.js to consume any API, ever, since the history of your browser?  Then look no further…

Why?

Checking out new JavaScript libraries and Frameworks can be scary.  Do you have a back-end already in place and just want to see what you can do on the front-end with Breeze.js and Knockout.js?  This sample / walk-thru is served only with an API, ESPN’s Developer API to be specific.  The only thing we do on the server here is server up our view from a Durandal.js starter app.  I choose Durandal.js because it is an extremely fast way to get a new project up in running, and provides a working app that needs only a few minutes to transform into an app of your own.

You can grab a working sample here of the finished code – http://github.com/PWKad/BreezeAPIOnlyKO (Note: this project uses NuGet package restore – read more about it here if you are unfamiliar http://docs.nuget.org/docs/workflows/using-nuget-without-committing-packages)

Breeze.js is an open-source JavaScript library from the established and well-respected team at IdeaBlade aimed at providing rich client-side data to your JavaScript apps.

  • Handles caching your data in the browser
  • Keeps track of changes to your JavaScript entities (and supports canceling those changes easily!)
  • Provides Linq-style queries in JavaScript

Knockout.js is an open-source JavaScript library created by Steve Sanderson to provide data binding all the way back to IE 6 (!!!!!)

  • Utilizes the MVVM pattern
  • Is an easy bolt-on to any project (Knockout is only concerned with binding data and keeping your view up-to-date)
  • Plug-ins are available to tackle any problem you have, and scale easily

Summary – 

The objective of this walk-thru is to help new to intermediate level JavaScript developers understand Breeze.js better and get a glimpse of the functionality when coupled with Knockout.js.  We will create a new project, add basic dependencies, set up Breeze to use with any API, create a JSON results adapter to map complex data structures, and bind our data to the view.

This is an open-source sample so if you see a problem feel free to make a pull request.  If you see a typo or something that just isn’t right feel free to leave a comment, or even send me a message either on here or on GitHub – pw kad

Technologies used –

Breeze.js – http://www.breezejs.com/

Knockout.js – http://knockoutjs.com/

Durandal.js – http://durandaljs.com/ – (Use the starter kit to get a JavaScript app up and running extremely fast)

Twitter Bootstrap – http://getbootstrap.com/2.3.2/

Getting Started – 

As a note, I use Visual Studio 2012 as my text editor for familiarity and ease-of-use. Let me know if you see any errors or have any helpful hints in the comments.

Create our new project – 

The Durandal Starter Kit is available at http://durandaljs.com/pages/downloads/ as a free download and also via http://www.asp.net/single-page-application/overview/templates/durandaljs-template as the SPA template (Note: Durandal 2.0 is now released, this project currently uses Durandal 1.2)

Open VS2012 and create a new ASP.NET MVC 4 application.  You can either create an Empty project and use NuGet to get the Durandal Starter Kit or install the template listed above and use Durandal SPA Project from the available templates window.

Creating a Durandal Starter Kit project

Creating a Durandal Starter Kit project’

We need to add a directory to our ‘App’ folder for the services we will use – App > services - This is where we will place our JavaScript files for getting data, creating the models, and any other JS files that are not view models.

Adding dependencies – 

Right click on our newly created project and choose Manage nuget packages… this will allow us to add our dependencies quickly and easily.

Choose the online option on the left of the pop-up to search online for available packages and in the top right search box type breeze to search for Breeze.js.  Install the Breeze for ASP.NET Web Api Projects package.  This package will add Breeze.js to the scripts folder, but it will also add some server-side stuff that we won’t be using.  (Basically if we were creating this project to serve up data as well as consume it we would need this additional library to make life easier, but since we will only be consuming the API we don’t need to worry about it)

Feel free to take a look in the scripts folder and check out our new dependencies.  Q.js is a promises library that Breeze uses to perform Asynchronous operations, such as querying.  If Sammy.js is in there, it was provided by our Durandal Starter Kit to help with routing and navigation (Note: Durandal 2.0 does not use the router plug-in including Sammy.js for routing nor navigation)

The only thing in the Controllers directory should be our Durandal controller, which will serve up our initial view.  The only thing in the Views folder are the splash page Durandal uses when loading up and an Index.cshtml file to host our Single Page App on.

Adding services – 

Inside of our services directory we need to add three JavaScript files (modules) –

First, let’s add our datacontext.js to the App/services folder.  This is requiring two services we haven’t finished setting up yet, (model and jsonResultsAdapter) so this won’t yet compile properly.

define(['services/model', 'services/jsonResultsAdapter'],
    function (model, jsonResultsAdapter) {

    });

Note on AMD (Asynchronous Module Definition) –

The define() statement defines the beginning of a module and is used to inject dependencies.  In this case we are using system/durandal and log system messages, so we require it and let our module know it can call it by using system

We will be using the model and jsonResultsAdapter in this module so we need to require them. Let’s add some basic functions to our datacontext

        var EntityQuery = breeze.EntityQuery;
        function returnResults(data) {
            return data.results;
        }
        var datacontext = {
        };
        return datacontext;
        function getLocal(resource, entityType, ordering) {
            var query = EntityQuery.from(resource)
                .toType(entityType)
                .orderBy(ordering);
            return manager.executeQueryLocally(query);
        }
        function queryFailed(error) {
            var msg = 'Error retrieving data. ' + error.message;
            console.log(msg);
        }

The EntityQuery variable will negate our need to call breeze.EntityQuery for each query we write.

The datacontext object we are creating is used to expose our functions to other modules that require it when it is returned.

getLocal is just a helper function to get local instances of an entity.

queryFailed is just a helper function logging errors in the console.

Setting up Breeze in our DataContext – 

We need to set up Breeze inside our datacontext

        var serviceName = "http://api.espn.com/v1/sports/baseball/mlb";
        var ds = new breeze.DataService({
            serviceName: serviceName,
            hasServerMetadata: false,
            useJsonp: true,
            jsonResultsAdapter: jsonResultsAdapter
        });
        function configureBreezeManager() {
            var mgr = new breeze.EntityManager({ dataService: ds });
            return mgr;
        }
        var manager = configureBreezeManager();
        model.initialize(manager.metadataStore);
        var metadataStore = manager.metadataStore;

This sets up a data-service, letting Breeze know that we don’t have any meta data available and will have to make our own and also let’s Breeze know we are going to use a custom jsonResultsAdapter.  We haven’t yet created it, but it is being required at the top of our datacontext in our define statement so it is available to us inside of it.

Next we need to create a model.js in our App/services folder –

define(['configuration'], function (config) {
    var DT = breeze.DataType;
    var model = {
        initialize: initialize
    };
    return model;
});

As you can see, we are revealing a function called initialize that initializes our entity models. We could have put this into our datacontext module but in the interest of the separation of concerns principle we want to keep this module separate.

    function initialize(metadataStore) {
        metadataStore.addEntityType({
            shortName: "NewsItem",
            namespace: "ESPN",
            dataProperties: {
                id: { dataType: "Int64", isPartOfKey: true },
                teamId: { dataType: "Int64" },
                headline: { dataType: "String" },
                description: { dataType: "String" },
                source: { dataType: "String" },
                imageSource: { dataType: "String" },
                imageCaption: { dataType: "String" },
                imageCredit: { dataType: "String" },
                link: { dataType: "String" }
            },
            navigationProperties: {
                team: {
                    entityTypeName: "Team", isScalar: true,
                    associationName: "Team_NewsItems", foreignKeyNames: ["teamId"]
                }
            }
        });
        metadataStore.addEntityType({
            shortName: "Team",
            namespace: "ESPN",
            dataProperties: {
                id: { dataType: "Int64", isPartOfKey: true },
                location: { dataType: "String" },
                name: { dataType: "String" },
                abbreviation: { dataType: "String" },
                color: { dataType: "String" }
            },
            navigationProperties: {
                newsItems: {
                    entityTypeName: "NewsItem", isScalar: false,
                    associationName: "Team_NewsItems"
                }
            }
        });
        metadataStore.registerEntityTypeCtor(
            'Team', null, teamInitializer);
        function teamInitializer(team) {
            team.fullName = ko.computed(function () {
                var loc = team.location();
                var name = team.name();
                return loc + ' ' + name;
            });
            team.showNews = ko.observable(false);
        }
    }

If you remember earlier in our datacontext we passed the metadataStore into the model.initialize() function. model.initialize(manager.metadataStore);. In the model we are adding types ‘Team’ and ‘NewsItem’. These entities have navigation properties linking them, which we set up with an association. This will allow us to easily bind the data in the view, and will remove the need to make additional calls to the server.

Finally, we want to register a custom property on the Team called ‘fullName’ which is a computed observable. We will use this in the view to represent a team’s location and name (ie. Texas Rangers) We are also setting an observable called showNews that we will use to flag whether the team’s news is being shown. Every time an entity is pulled in from the server or created these functions will be executed.

Now let’s set up our API calls in the datacontext

        var myAPIKEY = "qubdkem5nhuctxtxghkx32nm";
        var getTeams = function (teamsObservable, forceRemote) {
            if (!forceRemote) {
                var p = getLocal('Teams', 'Team', 'id');
                if (p.length > 0) {
                    teamsObservable(p);
                    return Q.resolve();
                }
            }
            var parameters = makeParameters();
            var query = breeze.EntityQuery
                .from("teams")
                .withParameters(parameters)
                .toType('Team');
            return manager.executeQuery(query).then(querySucceeded).fail(queryFailed);
            function querySucceeded(data) {
                var s = data.results;
                return teamsObservable(s);
            }
        };
        var getTeamNews = function (team) {            
            var parameters = makeParameters();
            var query = breeze.EntityQuery
                .from("teams/" + team.id() + "/news")
                .withParameters(parameters);
            return manager.executeQuery(query).then(querySucceeded).fail(queryFailed);
            function querySucceeded(data) {
                var s = data.results;
                var tempObs = ko.observableArray(s);
                // Since the news item has multiple categories and can be for multiple teams
                // we will set the team explicitly to the team we are searching for.
                // We could set it to each team, but this is a simple example.
                ko.utils.arrayForEach(tempObs(), function (newsitem) {
                    newsitem.teamId(team.id());
                });
                return true;
            }
        };
        function makeParameters(addlParameters) {
            var parameters = {
                apikey: myAPIKEY
            };
            return breeze.core.extend(parameters, addlParameters);
        }
        function returnResults(data) {
            return data.results;
        }

There is a lot going on here so let’s get down into more detail –

myAPIKEY is an API key provided by ESPN.  You can register for your own at http://developer.espn.com/

getTeams and getTeamNews are two functions that perform Breeze EntityQuery’s.  You can learn a lot more about structuring these queries on Breeze’s website, and I won’t cover exactly how they work here (would make this walk-thru much longer) but understand that these queries are structured to check the local cache for entities, and if there are none they will go hit the API.

makeParameters is an internal helper function that creates parameters.  The ESPN developer API requires a key to be passed, and we are extending any additional parameters passed in.  This will make our API call look something like this –

http://api.espn.com/v1/sports/baseball/mlb/teams/2/news?apikey=[apiKey will show up here]

Finally, there is a returnResults function that we will use to return the data.results from the callback.

We need to expose those functions to any other module that is requiring the datacontext, so adjust the object we are returning in our datacontext.

        var datacontext = {
            getTeams: getTeams,
            getTeamNews: getTeamNews
        };
        return datacontext;

Now that our datacontext is ready to go, we need to set up our jsonResultsAdapter.js

define([], new breeze.JsonResultsAdapter({
    name: "ESPN",
    extractResults: function (data) {
        var results = data.results;
        if (!results) throw new Error("Unable to resolve 'results' property");
        return results && (results.headlines);
    },
    visitNode: function (node, mappingContext, nodeContext) {
        if (node.headline) {
            if (node.images.length > 0) { 
                node.imageSource = node.images[0].url;
                node.imageCaption = node.images[0].caption;
                node.imageCredit = node.images[0].credit;
            }
            else {
                node.imageSource = '../content/images/blank_image.png';
                node.imageCaption = 'none';
                node.imageCredit = 'no credit';
            }
            node.link = node.links.web.href;
            return { entityType: "NewsItem" };
        }
    }
}));

This is a custom adapter we register in our datacontext to map the results from a complex JSON structure.  You can read more about how it works in the Breeze docs, but here is a basic breakdown –

name assigns a namespace.  If you remember earlier when we created the entity types in our model we assigned them to a namespace.

extractResults makes sure that the data returned has a results property and then checks to see if there is a headlines property.  If so, we create entities out of the results.  The reason we check for a headlines is because it is a unique property on the news objects being returned, so we know that if a result has a property headlines it should be mapped to a NewsItem.  When a news item is created we also need to map some of it’s properties.

Alright, that was a lot of JavaScript.  Let’s see some results –

Note – The below portion of this walk-thru doesn’t go into a lot of detail yet.  Give me a few days and I can explain it in more detail.

Delete the code inside your home.html in your App/views folder. Replace it with the below HTML –

<h3 class="teams-header">Team News - <img src="http://a.espncdn.com/i/apis/attribution/espn-api-black_150.png" /></h3>
<div class="accordion" id="sports-accordion" data-bind="foreach: teams">
  <div class="accordion-group">
    <div class="accordion-heading" data-bind="style: { backgroundColor: '#' + color() }" >
        <a class="accordion-toggle team-brief" data-toggle="collapse" data-parent="#sports-accordion" 
            data-bind="attr: { href: '#' + abbreviation() }" >
            <p><span data-bind="text: abbreviation"></span> - <span data-bind="text: fullName"></span></p>
        </a>
    </div>
    <!-- ko if: showNews() === true -->
    <h5 data-bind="visible: newsItems().length === 0">Fetching news...</h5>
    <div data-bind="attr: { id: abbreviation }" class="accordion-body collapse in">
      <div class="accordion-inner">
        <ul class="media-list" data-bind="foreach: newsItems">
            <li class="media">
                <a class="pull-left" data-bind="attr: { href: link }">
                    <img class="media-object" data-bind="attr: { src: imageSource(), title: imageCredit }" />
                </a>
                <div class="media-body">
                    <h4 class="media-heading" data-bind="text: headline"></h4>
                    <blockquote>
                        <p data-bind="text: description"></p>
                        <small>from <cite title="Source" data-bind="text: source"></cite></small>
                    </blockquote>
                </div>
            </li>
        </ul>
      </div>
    </div>
    <!-- /ko -->
  </div>
</div>

This is a Twitter Bootstrap accordion bound to our Knockout data.

Now replace all of the home.js code in our App/viewmodels folder with the below –

define(['services/datacontext', 'viewmodels/shell'], function (datacontext, shell) {
    var teams = ko.observableArray();
    var bindEventToList = function (rootSelector, selector, callback, eventName) {
        var eName = eventName || 'click';
        $(rootSelector).on(eName, selector, function () {
            var team = ko.dataFor(this);
            callback(team);
            return false;
        });
    };    
    var toggleTeamNews = function (team) {
        if (team.showNews() === true) { team.showNews(false); }
        else {
            if (team.newsItems().length === 0) {
                datacontext.getTeamNews(team).fail(queryFailed);
            }
            team.showNews(true);
        }
    };
    var viewAttached = function (view) {
        bindEventToList(view, '.team-brief', toggleTeamNews);
    };
    var initLookups = function () {
        datacontext.getTeams(teams).fail(queryFailed);
    };
    function queryFailed(error) {
        console.log(error.message + " - Query failed; please try it again.");
    }
    var activate = function () {
        initLookups();
        return true;
    };
    var home = {
        activate: activate,
        teams: teams,
        viewAttached: viewAttached,
        shell: shell
    };
    return home;
});

That’s it! Run our app and you will see a list of MLB teams, with the names on top of their team color returned from the server. Clicking on any of the teams will go fetch their news, and once we have fetched it Breeze is caching the NewsItem’s so we can hide or display them as we see fit, without having to hit the server again.

JavaScript Comparisons Pt. 1 – Data-Binding – Knockout.js vs Angular.js

I want to post some thoughts I have of various JavaScript libraries / frameworks in a short series that is direct and to the point.

While these posts may be full of opinions, they will be as truthful as possible and relevant to the point.

Test 1 – Data-binding

What do I expect from my two-way data-binding?

  1. Data is bound from the view to the * (controller, viewmodel, w/e)
  2. Binding is as efficient as possible (don’t waste my resources)
  3. Context / Scope is easy to understand
  4. Easy to use for a developer – the end-user doesn’t care how well data-binding occurs…

Comparisons – 

Knockout –

Pros –

  1. Data is bound to the view model.
  2. Binding occurs on blur, unless specificied
  3. Context is easy to set (with or foreach bindings give nested context easily)
  4. A dynamic nesting tree of data-binds is available, and trouble-shooting info is always available in console

Cons –

  1. Bindings can easily break when not handled properly (ie. binding to ID before it is available)
  2. Must use an additional data-bind attribute ‘valueUpdate: onKeyPress’ for per-key updates or use a custom binding handler
  3. Must understand $parent, $parents[1], and $root for contextual purposes
  4. Often a simple parans can be the difference between bindings working and two hours of work for a new developer

Angular –

Pros –

  1. Binding is simple and doesn’t require understanding of getters and setters
  2. Binding is on key press by default
  3. Settings what is within $scope is more explicit in the controller
  4. Binding requires no parans(!!!)

Cons –  (at least 1 week into a project)

  1. Notification of broken binds is not apparent
  2. Refactoring every binding in the view occurs every key stroke
  3. Contextual bindings aren’t as clear
  4. Bindings require twice as much mark-up (ng-click, ng-model, etc…)

In closing, Knockout serves a singular purpose (data-binding, rich UI’s) that can easily replace many of the functions that we as interface developers have relied upon jQuery for in the past.  Angular takes on many more tasks in addition to data-binding and therefore seems to have a very sufficient ability to bind data while taking it’s own approach.

Overall winner :  Knockout

Knockout knows what it’s main purpose is – data-binding and creating a rich UX.  Angular is good at both but seems to lean towards an all or nothing mentality, and does not support many dev favorites –

  1. Doesn’t support Knockout
  2. Doesn’t (easily) support Twitter Bootstrap