Identity Map for Backbone.js Models

If you are building a single page webapp, there is a good chance that your app has several screens. For example, one screen in Gmail lists all your emails and then when you click an email, another screen shows you the email thread and reply box etc. However, there is a problem here.

The Problem: Keeping models in sync across screens

In case of our app, SupportBee, we have a list of tickets and a screen to show an individual ticket that also loads up and shows the replies etc. To load up a list of tickets, we instantiate a TicketList collection

When we call fetch() on this collection, the tickets are fetched and a model is instantiated for each ticket in the collection and the corresponding view is rendered (code not shown here). On clicking the rendered ticket view, we initialize the ticket screen, and pass it a ticket object

This is where the problem starts. You now have two instances of the model for the same ticket id. One in the collection and one in the ticket view. If you change one, the change is not reflected in the other object until you save the state on the server and refetch the other one.

Let’s talk about a situation where this could be a very visible UI problem. Let’s say you are in the ticket screen and you ‘star’ the ticket. You update the star attribute of the object and thanks to the event binding, you update the view and a star shows up on the tickets’ page. However, if you go back to the tickets’ listing, unless you refetch the collection (and therefore reinitialize the models or refetch them individually), the listing view for the same ticket won’t show the star. The problem is more visible when you are caching rendered views and showing/hiding them on back/forward instead of re-rending them (which makes the app feel much slower).

One way to avoid the problem is to remember to pass the ticket object from the collection to the model and not instantiate a new one. However, as your app grows complex, you will not be able to keep a track of all the places where an object with a particular id is being initialized. For instance, in SupportBee, we also initialize ticket objects to show history in the sidebar which could again be intializing objects with ids that have already been initialized. Also a ticket could be present in two different collections fetched from different urls. For example, the same ticket (id) could be in /tickets/all_tickets and /tickets/my_tickets (better explanation here). The book-keeping just keeps getting harder as your app grows

The Solution: An Identity Map for Backbone.js

The solution is to make sure that at any time, only one object exists for a model with a particular id. However, this book-keeping should be done for you in the background and you should not have to worry about it when coding your app. Enter cached model

https://github.com/SupportBee/Backbone.js-Identity-Map/blob/master/public/javascripts/backbone_cached_model.js

Include the file in your project (after including backbone.js) and just add keepInSync: true when declaring your models. For example

Now, when you initialize a new instance of SB.Models.Ticket with a particular id, it will look for an already existing instance and if one does exist, start pointing to that one. So when you update an instance anywhere in the app, you can be sure that every view that is using this instance (and is listening to change events on this instance) will get updated right away.

Some good comments on HN thread.



blog comments powered by Disqus