Saturday, December 20, 2014

Initial thoughts on Responsive Single Page Applications

Is it feasible to build a responsive, mobile-first Single Page Application (SPA)? Here are my initial thoughts on the feasibility and challenges of building a responsive SPA. I plan to do more research in the coming weeks to hopefully answer this question more completely.

I was recently working on a mobile version of FuelMyRoute. I started by using jQuery Mobile and Backbone, but I found trying to use jQuery Mobile as a UI framework frustrating so I decided to give the Bootstrap CSS framework a try. I was able to convert my code over to Bootstrap fairly easily, but I started reading about and thinking about the responsive features of Bootstrap. This got me to thinking maybe I could make a responsive Single Page Application, one website that would work well on mobile devices and on desktop computers.

The benefits are pretty obvious I think. I don't relish the idea of maintaining two separate web applications. Especially when a lot of the non-UI code would be very similar. I could get a mobile site for my application while at the same time updating and modernizing the existing desktop site. That's a huge win!

I should probably define what I mean by responsive SPA. First of all, a Single Page Application is a web application where there is only an initial full page load. From that point forward AJAX is used to interact with the server to fetch data (e.g. get new mail) or to perform server side actions (e.g. send an email). The DOM is manipulated as appropriate in response to user actions and requested information. A responsive SPA is one where the UI of the SPA changes based on the size of the viewport, so that it is more appropriate/usable on smaller viewports but also can adapt and take advantage of more screen space on larger viewports. Responsive to me means also taking a mobile-first approach to web application development, where the mobile phone/small tablet use case is given primary (or at least equal) consideration.

But, I don't really see or hear much about responsive SPAs. I'm talking strictly about web applications, not web sites. If you are building, for example, a blog, then there is a lot of advice and support for making it responsive. However, for applications, it's different. The received wisdom is that it is better to have two different sites, one for mobile and one for desktop. Why? This is a question I want to answer along with the following:

  • What are the problems/tradeoffs that are encountered when making a responsive SPA?
  • Is it possible to use only CSS, only media queries to switch between different layouts for mobile and desktop usage? Or would it be necessary to have the JavaScript code be aware of whether the application is in mobile or desktop mode? (Ideally the responsiveness would be implemented in CSS only)

In this blog post I cover my initial thoughts about how a responsive SPA could work and what the challenges are likely to be.

Responsive Mobile UI Patterns

How would this work in practice? In this section I'll go over a couple of common mobile UI patterns and how they would adapt to a larger viewport.

Separate "pages" (mobile) vs in-page controls (desktop)

Sometimes the ideal view on mobile is very different from the ideal view on desktop. A common pattern here is that on desktop you have enough room to display all controls on the same screen, but on mobile you might want a separate full page view for certain actions. For example, imagine an airplane flight search application. On desktop you could have the fields for departing city and arriving city and dates on one page and display the results under those controls. On mobile, there isn't enough room for this so you would rather have a separate full page view for specifying search criteria.

In some ways, this is the least ideal situation. It is always better to be able to reuse the same controls and just style them differently using media queries. However, we must anticipate that this might not always be possible and think about what we could do in that situation.

To accomplish this, we could simply include the mobile full page alternate view in the page, hidden unless the width of the page is wide enough. By default, the mobile view is hidden.

#mobile {
    display:none;
    background-color: #fff;
    z-index:1000;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
}
@media only screen and (max-width: 480px) {
    .desktop-only {
        display: none;
    }
    #mobile {
        display: block;
    }
}

I created a very simple demo of this concept using the airplane ticket search example. In practice, you would want to have your application code add and remove CSS classes from a top level element to drive hiding and showing different views, but I've left that sort of thing out.

This "separate page" view could possible have a use on the desktop layout. You might have a situation where not all controls fit on the desktop layout so the same view to show all controls can be used on mobile and desktop. On mobile it might be styled as a full page view, whereas on desktop it could be styled as a modal popup.

Data binding is a little complicated in such a situation. Ideally, if the user changes the size of the browser window then switching from "mobile" to "desktop" or vice versa should be seamless from a user input perspective. Using the airplane ticket search example, if I am in the "mobile" view and I type in a Departing from city and then decide to expand my browser and get the "desktop" view, then ideally the Departing from control in the "desktop" layout should be populated with what I had typed on the "mobile" view. This means that I need to bind to the keypress event and update my model as each key is typed in Departing from and keep the "mobile" and "desktop" Departing from fields in sync.

Another challenge with this approach is that you, obviously, have two different views that have to be maintained. Arguably, this is one of the benefits of adopting MVC in the first place: your models are independent of views and models can have more than one view. However, it is still something extra that has to be maintained. It is probably better than having two separate sites though.

List and detail views on separate pages (mobile) vs both on the same page (desktop)

A common pattern with mobile apps is that you have a view(s) that display lists of data and tapping on a list item takes you to a view that displays details for that item. Examples include inbox listing/email detail and contact listing/contact details.

On desktop however there is enough space to show both the list and the detail, typically with the list on the left hand side and the detail view on the right. For example, an email client.

Media queries can be used to achieve both of these layouts. One nice thing here is that the same views are used on mobile and on desktop. Its just on mobile they are separate "pages" and on desktop they are all on one page.

How would you achieve this with media queries? I won't flesh out an entire example here, but I will outline how it would be done. The key I think is to have your SPA router add/remove CSS classes to a root element of the DOM. When the "list" class is added to the root element, the mobile media query will make just the list view visible, hiding the detail view. For desktop, there is no additional work needed. Likewise, when transitioning to the detail route, the "list" class is removed from the root element and a "detail" class is added. The mobile media query would then hide the list view and make the detail view visible. On desktop, the presence of the "detail" class on the root element might cause the currently selected item to be highlighted.

One challenge that this approach highlights (and this was true of the "separate view" UI pattern above) is that although on mobile the user is seeing only one view at a time, those other views are still there. For example, in an email client app, only the detail view may be visible, but the list view is still there and still responding to model updates. So for example, if you have a periodic check for new email, then the list view is updating to display new email received. This is more of a problem for mobile where off screen updates may cause performance to drag. On desktop the presence of alternate views that aren't on screen is unlikely to cause much of a problem.

Challenges with doing responsive SPA

Some of these challenges were mentioned above. Here I'll summarize them and also discuss how to address some of them. Not having experience developing a responsive SPA, this is mostly speculation about what I anticipate would be challenges building one.

Sizing of controls

It's possible to use media queries to size controls differently. You could for example make buttons large, more "tap friendly" on mobile screens and smaller on desktop screens.

However, one would have to assume that small screens are tapped on and large screens are clicked on. And that assumption isn't valid in a lot of cases. For example, a mobile phone might have a stylus that is able to make very precise touches. As another example, there are now several Windows 8 devices, some that are tablet/laptop hybrids, that are basically desktop devices but allow for touch interaction.

So I think a reasonable approach is to just size all controls for tapping. The tradeoff is that on desktop the controls are going to be "fatter" than necessary, meaning you won't be able to fit as many controls on the same page, but I don't think there is any reasonable alternative unless you want to aggravate uses with large touch screens.

Also, the fact that you can't have as many controls on a desktop view is not necessarily a bad thing. It will lead to a simplification of the UI, which will probably end up being easier to use.

Off screen view updates on mobile

I mentioned this one above. What can be done? For one, I think some testing and validation that this causes an issue is necessary first. Are DOM updates that are off screen as intensive as on screen ones for the browser to handle? Maybe, hopefully, not. I think it can very much depend on the application and might only affect certain views but not others.

If it becomes a problem I think one thing that could be done is to delay re-rendering the view if the view is off screen. Whenever it is ready to be brought back on screen the view could first be fully re-rendered. The problem here is that JS code now needs to know if the app is in "mobile" mode or "desktop" mode and so far I've been trying to keep the differences purely in CSS media queries.

Duplicate UIs

This one is mentioned above too. This is most applicable when the "mobile" version of the app needs a completely different view from the "desktop" version.

To mitigate this, first, obviously, try to use the same views for "mobile" and "desktop" and simply have them laid out differently.

Also it should be said that the duplication of effort and maintenance is probably not nearly so much as having two separate sites.

Also as mentioned above, there is also the data binding consideration: you'll want to keep the "mobile" and "desktop" views in sync at all times because the user could at any time transition from one to the other. In practice I think this requires only a little extra effort. See the discussion on listening for keypress events above.

Conclusion

Well that's it for my initial thoughts. I hope to investigate this further and try some of these techniques out.

Tuesday, June 24, 2014

A Backbone-JQuery Mobile skeleton project -

I've been playing around with integrating Backbone and jQuery Mobile. I'm planning on using them as the framework for implementing a mobile version of FuelMyRoute.

I started with Christophe Coenraets' backbone-jquerymobile sample app and made changes to bring it up to date with the most recent versions of jQuery Mobile and jQuery. I also made a couple different design choices. In this post I'll go over the changes I made.

jQuery.on

A couple of jQuery's event listener methods were removed in 1.9: live and bind. I updated several event listeners, like the mobileinit event listener, to use jQuery.on instead.

mobileinit

To configure jQuery Mobile, you set up a listener for the mobileinit event and set properties on the $.mobile object. Here's what I have so far.

$(document).on("mobileinit", function () {
    $.mobile.ajaxEnabled = false;
    $.mobile.linkBindingEnabled = false;
    $.mobile.hashListeningEnabled = false;
    $.mobile.pushStateEnabled = false;
    $.mobile.autoInitializePage = false;
});

This is identical to Coenraets' config except that I also added setting autoInitializePage to false. We'll get to that in a bit. The other settings are there to turn off the routing and link hijacking behaviour in jQuery Mobile so we can use Backbone for routing instead. Coenraets explains in his blog post on the topic and the jQuery Mobile project also has notes on integrating with Backbone.

autoInitializePage

By default jQuery Mobile will call initializePage() when it starts up. initializePage() is important because it initializes the pagecontainer and sets up navigation events. It will try to find the first div with data-role="page" and change to that page. If there isn't a page in the DOM, it will create an initial dummy page. This generally works out fine if you are using jQuery Mobile in the more typical way where you have multiple pages defined in the one page. However, with our Backbone integration, there isn't any page until the router runs and injects the first page into the DOM so it doesn't make sense to automatically call initializePage().

So what I did was set autoInitializePage to false. The Router will call initializePage() after it adds the first page to the DOM.

var AppRouter = Backbone.Router.extend({

    ...
    changePage:function (page) {
        // Render and add page to DOM once
        if ($('#'+page.id).length === 0) {
            $('body').append(page.render().$el);
        }
        if (this.firstPage) {
            // We turned off $.mobile.autoInitializePage, but now that we've
            // added our first page to the DOM, we can now call initializePage.
            $.mobile.initializePage();
            this.firstPage = false;
        }
        $( ":mobile-pagecontainer" ).pagecontainer( "change", page.$el,
                { changeHash: false });
    }
    ...
});

Managing Pages as Views

There are a couple different ways to go when managing page Views. You could remove them and recreate them each time or you could not remove them and just let jQuery Mobile hide and show them. Coenraets went with the "recreate each time" approach. I went with the approach of letting jQuery Mobile hide and show pages. My page Views are rendered and then added to the DOM once.

I did this because it seems a better fit with jQuery Mobile. Also I think there is a performance benefit on mobile to keeping DOM manipulations to a minimum.

However, I am still concerned about my page Views being bound to models even when they aren't being shown. Offscreen pages might re-render unnecessarily due to model/collection updates.

I might add to my router the ability to let pages know when they are being hidden or shown and that would allow a page to deal with offscreen rendering performance issues. One thing a page View might do is unbind its listeners when being hidden and rebind them when being shown again, which might be appropriate if intermediate events can be safely ignored. Another thing a page View might do when not being shown is continue to listen for events but defer any re-rendering until actually shown. I'll explore this more as needed, but I need a real world example to guide the design.

Enhancing pages

I did a little exploration into how you would handle jQuery Mobile enhancement if you re-rendered page. Basically, whenever render() is called, the page needs to be re-enhanced. jQuery Mobile will automatically enhance a page the first time it is changed to, but after that you need to call it yourself. I implemented a View base class I call PageView that has an enhance method. It allows you to implement render like so:

render: function () {
    // do some rendering
    // ...
    this.enhance(); // See PageView.enhance for details
    return this;
}

Keep in mind, though, re-rendering and re-enhancing the entire page is discouraged for performance reasons.

Minor stuff

  • $.mobile.changePage is deprecated. I switched to the change method on the pagecontainer. See the changePage method on the AppRouter above.
  • Instead of <div data-role="content">, use <div class="ui-content" role="main">
  • Contrary to Coenraets, I didn't find I needed to disable the first page transition, so I removed that code.
  • I also switched to project to use Bower and RequireJS to manage dependencies.
  • I never got around to dealing with transitions. Delegating routing to Backbone means you miss out on the declarative transition stuff in jQuery Mobile. I'm thinking that eventually I might add a transitions hash to the Router that would allow you to define page to page transitions, something like
    transitions: {
        'page1:page2':  'slide',
        // etc...
    }
    

Sunday, June 01, 2014

Git client setup

These are some settings and configuration I've found useful so far while working with Git.

Git global configuration

git config --global user.name "Marcus Christie"
git config --global user.email marc.christie@gmail.com

Can also set an editor to use for commit messages

git config --global core.editor gvim

Or with MacVim on OS X

git config --global core.editor 'mvim -f'

However, I think I prefer to edit commit messages in the terminal.

You can also use gvimdiff as a merge tool, but I haven't tried this yet.

Excludes file

Set up a global excludes file in ~/.config/git/ignore. My entries so far:

*.swp
.DS_Store
Session.vim

Whitespace

On Windows you want

git config --global core.autoclrf true

I think this is the default on Windows also, so this may be unnecessary.

Credential helper

This tip is mainly for when you are working with a repo over HTTPS, for example, with GitHub, and you authenticate with a username and password. You don't want to have to type passwords over and over. For this you can specify a credential helper that will securely store your password. On OS X:

git config --global credential.helper osxkeychain

On Windows, download git-credential-store. Then start up Git Bash, go to the directory where you downloaded the executable and then run it from the command line

$ git-credential-winstore.exe

Aliases

Shortcuts so you can run git ci -m "fixed bug" instead of typing out commit

git config --global alias.ci commit
git config --global alias.st status
git config --global alias.co checkout

Bash completions

On Windows, Git Bash comes with these already configured. On OS X, I downloaded git-completion.bash, dropped it in ~/bin and in .bash_profile I have

## Git completions
source ~/bin/git-completion.bash

Web viewer

On OS X:

git config --global instaweb.httpd webrick

Now you can start up a web viewer with

git instaweb

As a Mercurial refugee, I got used to hg serve so I like to have a web view.

Haven't tried doing this on Windows yet.

push.default

Set push.default to simple:

git config --global push.default simple

This setting affects what happens when you do a git push without specifing a remote or branch to push. With simple git will push the current branch to the upstream tracked branch. This mode also is the new default in Git 2.0.

Make sure that when you first push a branch to a remote server that you also set it as the upstream tracked branch. You can do that with the -u option:

git push -u origin mybranch

From then on every push can be done simply with

git push

merge.defaultToUpstream

Set merge.defaultToUpstream to true

git config --global merge.defaultToUpstream true

This setting affects what happens when you do a git merge without specifying a branch to merge from. With this set to true, git will merge in changes from the upstream remote tracking branch, which is generally what you want.

Anything else?

Are there any other things you do to configure Git on a new machine? Let me know in the comments or on Twitter.

Monday, May 26, 2014

Backbone Overview

Backbone is a JavaScript Model-View-Controller (MVC) library that can be used to create single page applications (SPAs). This article introduces the most important concepts in Backbone (Models, Collections, Views, Events, Router and Sync) and how they relate to building a client side JavaScript application.

What is Model-View-Controller (MVC)?

Model-View-Controller might be one of the most misunderstood software patterns ever. I'm not even sure I really understand what the original idea was (at least, what it meant to Smalltalk developers when first developed at Xerox PARC), and of course it has evolved over time into several different closely related ideas.

So this is far from authoritative, and I'll try to keep it simple. To me, MVC is a way to organize the implementation of a user interface in a way that separates concerns. Here, at a high level, are the various parts of a MVC framework:

  • Model - this is the representation of data that models the domain of interest. For example, for a TODO application, a model might be a TODO item. But Models aren't just data structures, they also contain the domain logic. A TODO item might expose a complete method that knows how to mark a TODO item as completed and properly manage any additional details related to task completion.
  • View - views are visual representations of models. A view reacts to changes in a model by updating the view to reflect that change.
  • Controller - controllers handle user input, both displaying views that the user requests and handling keyboard and mouse events. Controllers typically translate user input into updates on Model instances.

The goal is separation of concerns. Models know nothing of views; in a web application, models would have nothing in them relating to HTML or CSS, for example. Views know only how to represent a model, but they contain no business logic. Views respond to changes in a model, but know nothing of what the user is doing. Controllers are the only place in the application to handle things like navigation through the application, display of multiple views at once, transitions between views and translating user actions into model updates. But controllers don't contain business logic.

How does Backbone relate to traditional MVC?

The Backbone API has the following types:

  • Events
  • Model
  • Collection
  • View
  • Router
  • Sync

Where do each of these concepts fit into the traditional MVC pattern?

Backbone.Events are the primary mechanism for loose coupling between the models and views. Backbone Views bind to changes on the Models or Collections.

Backbone.Model and Backbone.Collection are the M of MVC: these types can be extended to provide the data and the implementation of domain logic for the application.

Backbone.View classes are a little V and a little C when compare to classical MVC. Backbone.View contains a render method that is responsible for generating/updating the DOM that is the view of the model, and for doing so it is typical to have a template. The template is more the V of MVC than the View class. Backbone.View also acts as a Controller in that it binds user events to methods on the View class that will update Models.

Backbone.Router is also fairly controller-ish in the traditional MVC understanding. The Router responds to user requests for specific URLs and can handle them by showing the right type of Views and by making sure that Model instances are available.

Backbone.sync is the RESTful persistence mechanism for saving/updating models. In Backbone, the models (Model and Collection instances) know how to persist themselves (which endpoint to call, how to serialize/deserialize, etc.). In that sense, sync is more in the M of MVC than anything else, but I'm not sure that persistence is in the scope of traditional MVC. (Another way to think of it is that sync is a translation layer between a client side model and a server side model, which are just two representations of the same domain object.)

Events

Backbone.Events provides standard event listening and triggering behavior.

  • Events.on(event, listener) adds a listener
  • Events.off(event, listener) removes listener
  • Events.trigger(event)

Backbone.Events can also be mixed in to other objects or classes via underscore's _.extend mechanism, and it is mixed in to Backbone.View, Backbone.Model etc. So these methods are available from most Backbone types.

By convention, Backbone Events are often namespaced. For example, you can listen to all changes on a model with model.on("change", listener), but you can also listen to just a change of the property "name" with model.on("change:name", listener).

But keep in mind there is no extra support in the implementation of Backbone.Events for namespaced events really, not like jQuery namespaced events. "change:name" works because Backbone.Model dispatches an event for each property that changes and also a "change" event. As another example, if you listen for "foo" and a "foo:bar" event is triggered, your event listener will not be called. You would have to trigger both "foo" and "foo:bar" in your code if you wanted to support namespacing.

One problem with setting up event listeners, especially with a View listening to changes on a Model, is that the object being listened to has a reference to the listener. For example, let's say you have a ContactView that is listening for changes to a Contact Model instance. If you dispose of the ContactView but forget to remove all of the event listeners, then the Contact Model still has a reference to your ContactView. This leads to a couple problems. First, your ContactView instance won't get garbage collected, so your app is now leaking memory. Second, whenever the Contact Model updates it will continue to call the event listener on the zombie ContactView and update it, so you have a lot of unnecessary code execution (and debugging this can get really confusing when you see code executed several times instead of just once as expected).

To solve this, Backbone.Events has two helpful methods, listenTo and stopListening. listenTo has this method signature

object.listenTo(other, event, callback)

Whereas you call on on the object you want to listen to, listenTo is called on the listening object. The advantage here is that the listening object can keep track of all of the listeners and can remove them all at once, which is what stopListening does.

One other note about events. In a Backbone app, you typically have two types of events: Backbone events and DOM events. DOM events are typically set up for a View in the events hash, which we'll get to later. When using the events hash, this refers to the View, pretty much what you expect. However, keep in mind, if you programmatically use the jQuery API to bind to DOM events then this refers to the DOM element.

Model

To create a Backbone.Model, extend the Backbone.Model class. You can specify default values for various properties.

app.Todo = Backbone.Model.extend({

    // Default attributes ensure that each todo created has `title` and `completed` keys.
    defaults: {
        title: '',
        completed: false
    }
});

To instantiate a Model instance, call the constructor and pass in the attributes for the instance. Any defaulted attributes that aren't specified will receive their default values.

Models have get and set functions for reading and writing attribute values. As mentioned above, calling set("myattr", newvalue) causes two events to be dispatched: "change:myattr" and "change".

Models also have a special property called id which uniquely identifies a Model instance. This can be set to an integer or UUID string. You pass it in the attributes hash when creating a Model instance. You can also set the idAttribute property on the Model class if you have a property on your Models that can be used to uniquely identify instances.

Collection

Create a new Collection by extending Backbone.Collection. You typically set the model property to the Model class of this collection.

var TodoList = Backbone.Collection.extend({

    // Reference to this collection's model
    model: app.Todo,

});

Collections redispatch Model events, so you can simply listen, for example, to "change" events on the Collection and you'll be notified whenever a Model instance changes.

You can add and remove Models to and from a Collection. These methods will dispatch "add" and "remove" events. Removing a model occurs when you call remove and pass in a Model instance that has the same id as a Model instance already in the Collection. Collections also have a reset method which updates the Collection all at once and dispatches just a single "reset" event, which is more efficient for example when initially loading a Collection.

Collections have several Underscore methods, like _.each, that you can call directly.

Collections are typically where you will set up the RESTful URL by setting the url property. You can then call fetch to retrieve all of the model instances in the collection and add them to the collection instance. We'll look at this more in the Sync section below.

View

Backbone View's always have a DOM element that they can render into. By default Backbone.View will create the DOM element for you, but if the DOM element you want to attach this View instance to is already in the page you can set el to a selector for that DOM element

var MyView = Backbone.View.extend({
    el: '#myview',
    //...
});

If you let Backbone create your DOM element, you can specify tagName, className, id and attributes to be set on that DOM element and Backbone will apply them. That is, if you specify a tagName of li and a className of todo-item, Backbone will create a <li class="todo-item" /> element in the DOM for you.

As a convenience, View's also have a $el property which is the jQuery wrapped reference to the View's DOM element.

A typical thing to do when you initialize a View is to pass it the model or collection instance you want it to bind to. In the View's initialize method, you can then bind to changes to that Model or Collection:

var TodoView = Backbone.View.extend({
    tagName: "li",
    className: "todo-item",
    initialize: function(){
        this.listenTo( this.model, "change", this.render );
        //...
    }
});

var todo = new TodoView({model: aTodo});

Besides binding to events on the Model, a View typically binds to DOM events. This is one of the controller-ish things a Backbone View does. To bind to DOM events, specify the events hash when creating the view. The keys in the events hash are in the form of 'eventName selector' and the value of each is the name of a function (or a function reference instead if you want).

var TodoView = Backbone.View.extend({
    events: {
        'click .toggle': 'toggleCompleted',
        // ...
    }
});

Inside of a DOM event handler, this refers to the View.

To actually render a view to the page, you provide an implementation of render. The job of render is to update this.el however necessary. The typical way to do this is to have a String based template, by default, an Underscore template. You'll set template in a view to a template function that takes an object with the properties of your model and returns a String of HTML with the model properties applied.

There are several approaches to how to organize your templates, but the simplest way is to put them in the web page inside a <script> tag with type set to text/template.

<script type="text/template" id="template-id">
    <input type="checkbox" <%= completed ? 'checked' : '' %> />
    <label><%- description></label>
</script>

In your View class, you can read in the template once with

//... inside View class
    template: _.template( this.$('#template-id').html() );

Then you can implement render like so:

//... inside View class
    render: function() {
        this.$el.html( this.template( this.model.toJSON() ) );

        // Anything else you might want to do...

        return this;
    }

Note that this.$ is a reference to the jQuery object. Also, render returns this so that parent View components can render child View components and then include them in themselves. For example, a container of these TodoView instances might do something like:

    render: function() {

        todos.each( function(todo) {
            var todoView = new TodoView({model: todo});
            this.$el.append( todoView.render().el );
        }
    }

When you are done with a View, you can call remove on it. This removes this.el from the DOM and also calls stopListening to remove all of its event listeners.

Router

Backbone.Router allows mapping URLs to actions and events. You create a router instance by extending Backbone.Router. You can specify a routes hash that maps url patterns to function names (or function references). This is somewhat similar to the events hash in a View.

Route url patterns can use the :param syntax of the *param splat syntax. :param is used to match a part of a URL. A splat matches the rest of the URL.

var Router = Backbone.Router.extend({

    routes: {
        '/search/:query': 'search',
        //....
    },

    search: function(query){
    }
});

As an example of how you would use a Router, consider an email application. When the user navigates to an email folder, the app will display a list of the email messages in that folder. You would set up a route for an email folder, maybe like so:

routes: {
    '/folder/:folderId': 'goToFolder'
}

Then when a user clicks on the email folder, you can call Router.navigate('/folder/123', {trigger: true}). If you want to handle updating the view to show the folder and just have the router update the URL, you would not pass {trigger: true}.

So now you need to kick things off for your app and have your Router handle whatever the current URL is (for example, if the user bookmarked that email folder link and navigated to it directly). To do that you call start on Backbone.history.

var appRouter = new Router();
Backbone.history.start({pushState: true});

Backbone.history is a kind of global router. It can make use of the History API, calling pushState if available, falling back to updating the URL fragment if not available. You have to pass {pushState: true} to start to make use of pushState; it is something you opt into in Backbone.

Sync

Backbone.sync handles persistence. By default, it supports RESTfully retrieving and saving models to a server, assuming a JSON API. In the simplest cases, you just define the base url on your Collections.

app.Library = Backbone.Collection.extend({
    model: app.Book,
    url: '/api/books'
});

When you call the following methods, the following URLs are invoked via AJAX:

  • Collection.create maps to a POST to the URL /api/books
    • Collection.create is a convenience function that saves the new model to the server and adds it to the Collection.
  • Collection.fetch maps to a GET to the URL /api/books
  • Collection.update maps to a PUT to the URL /api/books/{id}
  • Collection.delete maps to a DELETE to the URL /api/books/{id}

There's a lot you can customize however. Models can implement Model.parse to have custom response parsing logic. You can override Backbone.ajax to customize the AJAX logic. And you can go whole hog and override Backbone.sync to completely customize how to load/persist data, for example, using LocalStorage as a backing store for your app.

Additional Resources

Tuesday, May 06, 2014

Using Google's Places Autocomplete API to find directions

I'm currently integrating Google's Places Autocomplete API into the FuelMyRoute Android app. This way as soon as a user starts typing a destination, the location will autocomplete.

The Places Autocomplete API response is something like this:

{
"predictions" : [
    {
        "description" : "Barnes & Noble, East 3rd Street, Bloomington, IN, United States",
        "id" : "5c94b4b6d6ffdc0402451f0ccacd65d04c92b1be",
        "matched_substrings" : [
            {
            "length" : 6,
            "offset" : 0
            }
        ],
        "reference" : "CoQBeAAAAIcYT-uv-ryTSOBQ8Cl3W313c9fuh-J5KCY8N0k-QDVPWGD65lIgs9ZpXar6eIMJ3ey5HL-3d1yMChPIwcO9uDSFdyl_NeHcOgpSFVfqsbBh-E_F0QfVviuZHGPicPRLsGvEXP5jC6NuBZ2-0jQnxVzRNN-6W9XaXxFURgW115mLEhD3vouWuAImM-yWSat1opedGhROZAG_tNmuisZo82H0DOvw-yqj5Q",
        "terms" : [
            {
            "offset" : 0,
            "value" : "Barnes & Noble"
            },
            {
            "offset" : 16,
            "value" : "East 3rd Street"
            },
            {
            "offset" : 33,
            "value" : "Bloomington"
            },
            {
            "offset" : 46,
            "value" : "IN"
            },
            {
            "offset" : 50,
            "value" : "United States"
            }
        ],
        "types" : [ "establishment", "geocode" ]
    },
    ...

I was then feeding the description (in the case above, Barnes & Noble, East 3rd Street, Bloomington, IN, United States) as the destination to Google's Directions API.

However, this turns out to only work sometimes. The description returned by the Autocomplete API is not necessarily detailed enough to resolve to a geocoded address.

What you need to do instead is take the reference field for a location and pass it to yet another API, the Place Details API. These request URLs look like

https://maps.googleapis.com/maps/api/place/details/json?\
    reference=CoQBeAAAAIcYT...&sensor=true&key=API_KEY_HERE

The result of which looks like this

{
"html_attributions" : [],
"result" : {
    "address_components" : [
        {
            "long_name" : "2813",
            "short_name" : "2813",
            "types" : [ "street_number" ]
        },
        {
            "long_name" : "E 3rd St",
            "short_name" : "E 3rd St",
            "types" : [ "route" ]
        },
        {
            "long_name" : "Bloomington",
            "short_name" : "Bloomington",
            "types" : [ "locality", "political" ]
        },
        {
            "long_name" : "IN",
            "short_name" : "IN",
            "types" : [ "administrative_area_level_1", "political" ]
        },
        {
            "long_name" : "United States",
            "short_name" : "US",
            "types" : [ "country", "political" ]
        },
        {
            "long_name" : "47408",
            "short_name" : "47408",
            "types" : [ "postal_code" ]
        }
    ],
    "adr_address" : "\u003cspan class=\"street-address\"\u003e2813 E 3rd St\u003c/span\u003e, \u003cspan class=\"locality\"\u003eBloomington\u003c/span\u003e, \u003cspan class=\"region\"\u003eIN\u003c/span\u003e \u003cspan class=\"postal-code\"\u003e47408\u003c/span\u003e, \u003cspan class=\"country-name\"\u003eUnited States\u003c/span\u003e",
    "formatted_address" : "2813 E 3rd St, Bloomington, IN, United States",
    "formatted_phone_number" : "(812) 331-0669",
    "geometry" : {
        "location" : {
            "lat" : 39.165632,
            "lng" : -86.49625899999999
        }
    },
    ...

You can get the latitude/longitude from geometry.location (also the formatted_address would likely work as well as a destination in a Directions API request). Now you can make a Directions API request with an exact destination.

Monday, May 05, 2014

Disjunctive Normal Form: Figuring out what part of a WHERE claused matched

At work we have a way for filters to be defined by users and for these filters to trigger other actions (at a very high level, the details aren't so important for the purpose of this blog post). These filters translate to SQL WHERE clauses. I'm currently working on a project to figure out, when a filter matches, what was it in the filter that actually matched.

The problem is this. You can have a filter with arbitrary boolean logic in it. To take a very simple example, consider the following filter as WHERE clause:

WHERE
    COLUMN1 = 1
    AND (COLUMN2 = 3 OR COLUMN3 = "foo")

When you have a match, you might wonder: Was it COLUMN1 and COLUMN2 that caused the match, or was it COLUMN1 and COLUMN3? (or maybe all three matched the conditions).

So we can break this down into two problems.

  1. What are all the possible sets of conditions that could satisfy the filter?
  2. Which of these sets of conditions are matched?

For the first problem, we can transform the filter expression into Disjunctive Normal Form (DNF). Disjunctive Normal Form is an OR-ing of ANDs, where negation is pushed down to the "leaves" of the boolean expression tree (in other words, negation applies only to literals).

Our example above rewritten in DNF becomes:

WHERE
    (COLUMN1 = 1 AND COLUMN2 = 3)
    OR
    (COLUMN1 = 1 AND COLUMN3 = "foo")

For the second part of the problem, I can split apart this filter and apply each part separately. If (COLUMN1 = 1 AND COLUMN2 = 3) matches, then I know that those conditions are true for that match. Likewise for (COLUMN1 = 1 and COLUMN3 = "foo"). Now I know what conditions were actually matched for a given match.

How to get to Disjunctive Normal Form?

Based on what I've been reading, there are two main ways to convert a boolean expression to DNF.

  1. Algebraic - Use the laws of Boolean Algebra, like De Morgan's law, to rewrite the boolean expression into DNF.
  2. Algorithmic - Generate a truth table, or use an algorithm like Quine-McCluskey.

Algebraic approach

The approach is pretty simple and is a two step process:

  1. Convert first to Negation Normal Form. This uses double negation elimination and De Morgan's Laws to push negation down to the leaves of the boolean expression tree.
  2. Pull the ORs to the top of the expression tree using the Distributive Law.

(Credit for this approach goes to this StackExchange answer.)

Algorithmic approach

I'll give an example of this approach using a truth table. Let's stick with the current example:

COLUMN1 = 1 COLUMN2 = 3 COLUMN3 = "foo" COLUMN1 = 1 AND (COLUMN2 = 3 OR COLUMN3 = "foo")
T T T T
T T F T
T F T T
T F F F
F T T F
F T F F
F F T F
F F F F

You can see that the first three rows of this table evalute to true, so we can convert each row to a clause in our DNF:

WHERE
    (COLUMN1 = 1 AND COLUMN2 = 3 AND COLUMN3 = "foo")
    OR
    (COLUMN1 = 1 AND COLUMN2 = 3 AND COLUMN3 != "foo")
    OR
    (COLUMN1 = 1 AND COLUMN2 != 3 AND COLUMN3 = "foo")

As you can see, this naive approach results in a rather large DNF, and this is a very simple example. To reduce this DNF to a simpler equivalent expression, you can use something like the Quine-McCluskey algorithm.

Which approach to take?

I think in general the algebraic approach is preferred if you have a boolean expession tree already for your boolean function. You can fairly simply rewrite this tree into DNF. This is the case with these SQL filters I'm working with.

If however the inputs to your boolean function are well defined (whether they are true or false) but you don't know the implementation details of your boolean function, then an algorithmic approach is going to be more appropriate.

For me, I'm going with the algebraic approach. I have the boolean expression tree for these filters I'm working with, but the "inputs" to the filter aren't so easy to get at without a lot of work. By that I mean that our system generally doesn't know that a row in a table has COLUMN1 = 1, etc., and rather the filter is just evaluated directly against a table. It would take a fair bit of work to translate criteria like COLUMN1 = 1 into boolean inputs, and to represent the criteria as boolean literals in the filter expression.

Sunday, May 04, 2014

Git: How to delete a branch

How do you create a temporary branch to do some work in (e.g, a feature branch), and then when you have merged it to master and are done with it, how do you delete it, both locally and remotely?

Let's create a simple repo to experiment with. We'll add just one file to it.

mkdir myprj
cd myprj
git init
vim README.md
git add README.md
git commit -m "adding a README"

Okay, this will be our "central repo". Let's clone it, create a branch and do some work on it.

git clone myprj myprj2
cd myprj2
git branch install_notes
git checkout install_notes
vim README.md
git commit -a -m "Adding some installation notes"
git push origin install_notes
# Oops, forgot to set upstream
git push --set-upstream origin install_notes

The branch install_notes now exists locally and in the "central repo". Let's merge it and then delete it locally and also remotely. Before we get started though, here are all of our local and remote branches:

marcus$ git branch -avv
    * install_notes                6a5ed4c [origin/install_notes] Adding some installation notes
    master                       27c8a05 [origin/master] adding a README
    remotes/origin/HEAD          -> origin/master
    remotes/origin/install_notes 6a5ed4c Adding some installation notes
    remotes/origin/master        27c8a05 adding a README

First, let's switch back to master and try to delete install_notes:

marcus$ git checkout master
    Switched to branch 'master'
    Your branch is up-to-date with 'origin/master'.
marcus$ git branch -d install_notes
    warning: deleting branch 'install_notes' that has been merged to
            'refs/remotes/origin/install_notes', but not yet merged to HEAD.
    Deleted branch install_notes (was 6a5ed4c).

Hmm. It gave a warning, but proceeded to delete the branch anyways. Well, let's try to check it back out.

marcus$ git checkout -b install_notes origin/install_notes
    Branch install_notes set up to track remote branch install_notes from origin.
    Switched to a new branch 'install_notes'
marcus$ git checkout master
    Switched to branch 'master'
    Your branch is up-to-date with 'origin/master'.
marcus$ git merge install_notes
    Updating 27c8a05..6a5ed4c
    Fast-forward
    README.md | 4 ++++
    1 file changed, 4 insertions(+)

Okay, now we can delete the branch.

marcus$ git branch -d -r origin/install_notes
Deleted remote branch origin/install_notes (was 6a5ed4c).
marcus$ git branch -d install_notes
Deleted branch install_notes (was 6a5ed4c).

And now my branches look like this

marcus$ git branch -avv
    * master                6a5ed4c [origin/master: ahead 1] Adding some installation notes
    remotes/origin/HEAD   -> origin/master
    remotes/origin/master 27c8a05 adding a README