views:

122

answers:

2

I understand that the title is a little vague and all-encompassing, so please let me try to narrow it down.

What I would like is advice on how to develop a mostly Ajax website, where portions of the UI are asynchronously loaded. Here's the catch: I'd like to have the browser back/forward buttons work intuitively - something that Facebook has accomplished very well.

Does anyone know what libraries and design patterns Facebook uses? Considering that they've done an excellent job, it would make a lot of sense to use them as a model, as opposed to trying to reinvent the wheel.

I've noticed that there's a lot going on in the url hash:
http://www.facebook.com/home.php?#!/home.php?sk=bd

I'm sure that there has to be a good reason for them doing this, and I wouldn't be surprised if it's something I could make use of. Can someone point out what they're accomplishing here, and what each parte of that ?#!/home.php?sk=bd is used for? I'm particularly surprised to see the home.php?sk=bd, when the original page that was loaded is home.php - is that perhaps their way of allowing links to specific "pages", despite the entire thing being served by home.php?

It's not so important that each question be addressed - I'm really just trying to communicate what it is that I'm having trouble understanding - the "bigger picture", if you will. If someone can give me a more holistic answer, that would be awesome (especially if you can tell me how this might accomplished using ASP.NET MVC).

Thanks in advance!

+1  A: 

We have built an app like you describe, here's what we did finally:

  1. The server discuss with the browser exclusively through JSON data.
    XML messaging is ok as well but harder to handle in all the browsers.
     
  2. The frontend is only built from static files: HTML, CSS, JS. The server handles only the security and the transactions to the database.
    All the HTML is rendered client-side with pure.js a Javascript templating engine, it separates cleanly the HTML view and the JS logic.
     
  3. For the navigation we use the hash key (#) as the status of the app. eg: what person the user is looking at. After testing many solutions, we ended up building a very simple one, using a setInterval polling of 120ms, that check the value of the hash key for any change.

The app is very responsive and the navigation very intuitive.

Mic
+1  A: 

I'm using technique's similar to Mic's. Essential parts of my toolkit:

  • A javascript build system. I use ant to roll all of my little js source files in to one big final file. This way I can keep my files as small as I want, not worrying about having to import 100's of separate scripts into the browser.

  • A template engine. I'm using Trimpath and have been pretty happy with it. In addition to Trimpath, I've developed my own system for handling includes and template inheritance which I use to process my template code before handing off to Trimpath.

  • A preprocessor for template files which converts multi-line strings into valid javascript. This way, I can write regular, multi-line html, hand it to my script which converts it to javascript, and can then roll my templates into the master js file, rather than having to serve them separately.

  • Techniques for dealing with asynchronous calls. There are many situations where, in order to render a screen, I need to make sure dataA, dataB and dataC are loaded. Rather than create a server-side call "fetchDataABandC", I have a client-side function with which I can specify "do A, B, and C, and call this callback when all have finished". I can also specify "do A then B then C, sequentially". Another thing I'm finding myself doing a lot of is setting up functions so that they can be easily turned in to asynchronous calls. So, generally, any time I have a function which gets data, rather than having that function return a value, I pass a callback. If, for example, I'm storing some data in a cookie, and later I decide to move that data to the server, if the functions I"m using to get and set that data are callback-based, it's easy to swap out the implementation with a server call.

  • A caching mechanism. All of my server calls are accessed through a single "Service" object. I have a caching object which wraps the Service object, duplicates every single method of the object, and adds an additional method called {methodName}Cached. So if the original service object has a method called "getSubscriberDetails", the cache object will dynamically create a "getSubscriberDetails" method for itself, which links back to the original, and also a "getSubscriberDetailsCached" method, which will return cached data if it exists, but will otherwise call the original function (and cache the data that it returns). Internally, the cache stores data using keys generated by method name and method arguments, so if I call cache.getSubscriberDetailsCached("subscriber1"), that is distinct from cache.getSubscriberDetailsCached("subscriber2").

  • An event system to communicate between widgets. The software I'm building is a contact management system. If I have a widget which lists all of a user's contacts, and another widget which allows the user to add and remove contacts, the contact list widget needs to be notified which a change happens. Rather than having the "add contact" widget hold awareness of every single other piece of functionality which depends on the list of contacts, I use jquery's event system to send out a "contactsChanged" event to ever dom object which has the class "contactsChangedListener". So, my "contact list" widget has the class "contactssChangedListener" in its outermost div, and I attach a listener inside the "contact list" controller via jQuery.bind. When a "contactsChanged" event gets sent out, my "contact list" widget knows that it needs to refresh its list of contacts.

  • Hash-based navigation. I'm using the jquery hashchange plugin to help me listen for hash changes. Every screen is associated with a separate url, and the way you move from one screen to another is by changing the url. I don't use straight links though. Every navigation action passes through a navigator object which can be configured to do things like pop up an alert message telling the user that they're about to lose unsaved changes.

morgancodes
Interesting... it looks we are in the same widget kind of quest ;) Though, I didn't choose the DOM but a JS object to register events. And I was not that convinced by trimpath when testing it, and built pure.js for that.
Mic
Widget Quest! The thing I like about using dom for events is registering and deregistering listeners is very automatic. If you delete a widget, you don't have to worry about cleaning up references to that widget, it just happens automatically. Pure.js is yours? Cool!
morgancodes