Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Help learning from pivotaltracker's javascript, looking for a high level breakdown

My javascript skills are fairly basic, I can work with jquery etc. but when it comes to building a site like pivotaltracker I wouldn't know where to begin!

Was hoping someone could help break down their javascript architecture and explain at a high level how they went about designing their js frameowork to make a gmail like design where it is purely javascript driven (at least I think it is).

Things like:

  1. layout wise, is there a single div container that loads the different panels?
  2. Does it keep a browser copy of all the stories, and use javascript templating to build the html?
  3. how are the various objects designed
  4. I think this is a major one, how are the events wired up, is it a global event that bubbles up?

I think the interesting thing is that there are allot of DOM elements on a page with all the user stories grouped together etc., so they must have done some cool performance techniques especially around events etc.

like image 433
Blankman Avatar asked Jul 16 '11 15:07

Blankman


2 Answers

I think your question is more about understanding MVC (model-view-controller) patterns in javascript. I think you should update your question to reflect that.

Something like 'Help understanding MVC patterns in javascript'.

It's hard to distil the concept of what that looks like in javscript without providing a demo use case with examples and a detailed walk through of the code. I know that is essentially what you have asked for, but I think that is out of the remit of StackOverflow.

MVC patterns are fairly familiar and widely used in server side frameworks, for Example.

  • PHP has CodeIgnighter
  • Ruby has Rails
  • Python has Django
  • Java has Spring
  • Plus many, many more variations for each language.

The MVC pattern is closely linked to the concept of OOP ( object oriented programming ). While it is not fundamental for a language to be object oriented in order to follow the MVC pattern. Many MVC frameworks tend to be built following OOP methodologies to the extent the language will allow.

This is one of the reasons I think the MVC concept is less prevalent in front-end development. For a long time Javascript has been a fairly mis-understood as a language. As a result it is only fairly recently that people have been applying the principles of OOP into javscript.

The improvement in browser conformance and libraries like JQuery has had a lot to do with this. Having the ability to focus less on the frustrations of inconsistencies in the DOM, Has allowed people to realize the core aspects of the language itself.

( Many people believed and still do, that browser inconsistencies are a fault of the JavaScript language, not the browser vendors implementation of the DOM. Which is the root cause behind the mis-understanding of Javascript. )

With that little rant out of the way, I will have a crack at giving you a really high level interpretation of MVC.

In MVC frameworks the creation of Models, Views, Controllers and how they interact is predefined. They do this to keep project clean and following the same structure throughout. The benefits of this are..

  1. It's easier for new developers coming to the project to understand what's going on.

  2. The more time you spend working in the framework, the more familiar you will become with the api's. So it speeds up development time.

  3. The common structure and api's make it easier for you and others to maintain the codebase.

To understand how they do this in javscript you need to understand how constructor functions, prototypes and objects work. These are some of the fundamentals of the core JavaScript language and eloquent JavaScript is a good place to get started.

To start I don't think the term MVC is quite in the right order to aid visualising the flow of the internal processes. Whether this is intentional or not I don't know, I guess different people perceive things differently, but it seems to me that MVC is just easier to say and sounds better.

I prefer to think of it as CVM.

The key point of MVC frameworks is the separation of logic.

CONTROLLER >> The controller, is the functional part of the application, each controller deals with a specific aspect of user interaction. It then manages how that interaction should be handled by passing changes to the models and views, based on the input it received.

MODEL >> The Model is all about data. It only has one job, Model the data. So the Model would normally take data and validate or change it's representation. The Model also takes care of the CRUD operations ( Create, Read, Update, Delete ). You normally have a separate model for the different types of data running through your app. e.g. Users, Comments, Posts.

VIEW >> The View is the visual representation of the operation. It takes data from the model and generates the visual output. While the view generates the visual output, it is common that the view itself does not do the job of rendering it. It simply returns the visual representation to the controller for rendering. Views are not associated with whole pages, each view represents a different visual aspect of the application e.g. Sign in Dialog, New Comment etc.

By separating out different parts of an application like this. Many of the parts become interchangeable and re-usable by different controllers.

In backend MVC framework the user interaction they respond to is normally a page request. So the controllers listen to requests coming from the client. They use the url and query params to work out which controller is responsible for dealing with that request.

e.g. http://myapp.com/users/ >> user Controller

The controller may then use any subsequent part of the url to define what models and views it should use to respond.

e.g. http://myapp.com/users/new/ >>  user Controller renders the newUser View

Server side MVC frameworks use URL fragments to respond to user interaction, because they don't have access to the user interaction directly ( e.g. the server can't respond directly to a mouse click ). So it is more by force than choice that server side applications work this way.

In Javscript however we do have that luxury. We can add event handlers to parts of interface and respond directly to user interaction. This pattern is familiar to virtually every JavaScript user.

e.g. ( using jQuery )

// Create and event handler
$('.myButton').bind('click', function(event){
    // Do some work when this event is fired.
});

It just so happens however that this ability to micro manage user interaction, is an inefficient approach in JavaScript intensive applications ( also known as single page web apps ). You end up with spaghetti code and duplication of functionality. As this approach tends to lead to someone encapsulating all the functionality into the function dealing with the interaction.

e.g.

$('myButton').bind('click', function(event){
    var self = $(this);
    event.preventDefault();

    $.ajax({
        url: self.attr('href'),
        context: self.parents('.ResponseContainer'),
        success: function(data){
            self.addClass('.workDone');
            for( key in data ){
                $(this).append('<li>'+data[key]+'</li>')
            };
        }   
    }); 
});

So JavaScripts ability to deal directly with interaction, actually becomes a disadvantage. Having a global object such as the URL to respond to, makes modelling and separating parts of the application much easier to handle and conceptualise.

In theory you could create your own global object to store the application state and monitor for changes in your Controllers. However for most applications this is an unnecessary pursuit, it turns out that the URL object is both simple and highly effective for this operation. Because the URL contains a form of state in it's fragments, people can jump straight to specific parts of your application. If you implement your own object to do the job of the URL, the application would not have any knowledge of state prior to it's load. Any state at runtime would also be lost as soon as the page is closed. So the URL provides an excellent mechanism for persistent and transferable state ( as the URL can be shared ).

Therefore in most JavaScript MVC frameworks they use the URL over directly handling events. This presents some problems however, in order to change the URL a link has to be clicked. The browsers default behaviour is to send a request to the server for the new page and re render the entire page.

This is obviously not what we want to happen. So in order to prevent this MVC frameworks use a couple methods to alter the browsers default behaviour. The first mechanism is to prevent default on all link clicks.

e.g.

$('a').bind('click', function(event){
    event.preventDefault(); 
});

// This prevents any link clicks from firing the browsers default action
// of making a request to the server and reloading the page.

In order to change the URL we have to update the window.location object to point to the URL contained in the the links href attribute. However just changing the window.location will still cause the page to be reloaded. In order to over come this we actually change the url to use the hash fragments e.g. http://myapp.com/#/users. When the browser see's a hash in the URL it does not reload the page. Historically the hash was used to navigate to a section of content within the existing page.

Hash updates also go into the browsing history, allowing you to navigate using the browsers back and forward button.

e.g.

$('a').bind('click', function(event){
    event.preventDefault();
    var el = $(event.currentTarget);
    window.location.hash = el.attr('href');
});

// A real use case would be much more complex than this.
// This code assumes you have a link structured like 
// <a href="/new/user">Sign up</a>

A separate function will monitor for changes in the hash fragment. This maybe in the form of a setInterval() on the location.hash that compares the previous fragment to the current one, or a custom event fired by the above function.

In order to allow the controllers to respond to the correct URL ( also referred to as Routes ), typically naming conventions on objects or methods are used.

e.g.

//Create your controller to listen to '/user' fragments
var users = new Controller('/users');

// function to run on '/user' fragment changes
users.On = function(reqParams){
    // do some work to respond to http://myapp.com/#/users; 
};



// create a Controller as a method of users, to respond to '/users/new'
users.new = new Controller('/new');

// function to run on '/user/new' fragment changes
users.new.On = function(reqParams){
    // do some work to respond to http://myapp.com/#/users/new          
};

I'm not going to go into more detail, MVC frameworks provide different ways to implement and structure your application. Also because JavaScript does have the ability to directly respond to user interaction, that power shouldn't be completely ignored. So in some JavaScript MVC frameworks they slightly taint the pure MVC concept, to allow for deeper interaction control.

I came across this video tutorial by Ben Nadel exploring the MVC concept in single page web apps. It an extremely detailed walk through of how to structure an app. And also gives some great JavaScript authoring tips.

  • http://www.bennadel.com/blog/1730-Building-Single-Page-Applications-Using-jQuery-And-ColdFusion-With-Ben-Nadel-Video-Presentation-.htm

Some Javascript MVC Frameworks

  • http://documentcloud.github.com/backbone/

  • http://maccman.github.com/spine/

  • http://www.bennadel.com/resources/projects/cormvc/demo/index.htm#/ by Ben Nadel as a result of the presentation ( might be the best choice after watching the video ).

  • http://javascriptmvc.com/

  • http://knockoutjs.com/

An overview of a few of the frameworks mentioned above.

  • http://www.protectedmethod.com/blog/4d49b82942ba7864ce00000d/knockout_vs_javascriptmvc_vs_backbone

And don't forget to read eloquent JavaScript if you haven't already

  • http://eloquentjavascript.net/

I hope this is enough info for you to get started.

like image 164
AshHeskes Avatar answered Oct 13 '22 22:10

AshHeskes


Pivotal Tracker UI (and js) is very similar to Google Wave (Wave in the Box) Wave protocol specification So I think it has following architecture.

Main page consists of html and js loader. Html is simple - just a div with no content. Loader runs when the page is loaded, just like that

$(document).ready(function(){
        $("#main_holder").buildPage("home"); // jquery example
});

This function runs 2 tasks:

  • load data (through AJAX e.g.)
  • build UI with loaded data

Loading data is clear operation. Building UI is more complex. UI builds with simple controls - widgets (or some sort of widgets). Each widget has a code to build itself, and initialize event handlers. Every loaded widget is registered in a loader (or mediator), so it can access to other widgets data through the loader.

For building html for each widget templates is used (some kind of JSP templates). Example of template

    <li class="task_<%=id%> <%= readOnly ? 'readonly' : '' %>">
  <% if (!readOnly) { %>
    <input type="checkbox" name="task[complete_<%=id%>]" value="1" <%= complete ? "checked='checked'" : "" %>/>
    <div style="display:none"><textarea class="autoresize expand17-10000" name="task[description_<%=id%>]"><%= description %></textarea></div>
  <% } else { %>
    <div><%= position %>.</div>
  <% } %>
  <label <%= complete ? "class='completed'" : "" %>><%= Element.formatText(description) %></label>
  <% if (!readOnly) { %>
    <ul class="actions">
      <li class="edit"><a href="#" title="Edit Task"><img src="<%= edit_img_src %>" /></a></li>
      <li class="delete"><a href="#" title="Delete Task"><img src="<%= delete_img_src %>" /></a></li>
    </ul>
  <% } %>
</li>

Template compiles by the template engine and becomes a pure html code.

Event handlers is not global. Every widget create event handlers by itself. If it's a global event, which need to be fired on each widget, then the loader (mediator) fires it by calling trigger method (for jquery) on each widget registered in it's list.

Various objects designed as a associative arrays. Like

    org.pivotal.model.vo.VariousObjectVO = new Class({
      /**
       *
       * @param {Long} id
       * @param {String} name
       * @param {Map<String, String>} fields
       * 
       */
       initialize: function(){

       },
       id: null,
       name: "",
       fields: {}
    });

So you can keep any count of fields with any count of values.

Hope it helps.

Regards, Sergey

like image 44
Sergey Galchenko Avatar answered Oct 13 '22 23:10

Sergey Galchenko