The MVC in JavaScriptMVC

DISCLAIMER: I recently started using JavaScriptMVC and got to like it a lot. These blogposts are a way for me to document my learning process of the framework. There is the possibility that I misinterpreted certain principles or abusing the framework at some points. So don’t be afraid to ask questions or set things straight in the comments.

In my last post I outed my love for JavaScriptMVC. I got the remark of James that a library that consolidates javascript files doesn’t make it MVC and that is absolutely correct. But JavaScriptMVC is much more then just javascript file consolidation. In this post I would like to show the MVC capabilities of the JavaScript framework and how a typical application built with JavaScriptMVC is structured.

When you download the framework and unzip it to the folder of your choice you get the following file structure:

Picture 1

We will touch some of the folders and files in this structure. The js.bat is a very important one because we will be generating a lot of the files via this command file. As an exercise I will build a fairly simple tabbed interface using JavaScriptMVC. I’ve put the result of this tutoria online. The initial mockup of the application looks like this:

 Picture 2

Before we can start writing code, we have to create an application. You can do this by navigating in a command prompt to the folder where you unzipped the JavaScriptMVC bytes and executing the following command:

js jmvc\generate\app tab_manager

The batch command has generated a couple of application files. This batch file will also be used to generate models, controllers, views and much more. Before we go any further let’s digg into how JavaSciptMVC uses the MVC pattern. From their website we get the following explanation for models, views and controllers:

Model
wrap an application’s data layer, this is done in two ways:
- request data from and interacting with services.
- Wrap service data with a domain-specific representation

Controller
event delegation that helps logically organize your event handlers

View
Is used as a templating library, it builds html strings from JSON

While using the framework I took the liberty to use parts a little different. Controllers I use as defined for event delegation. But models I don’t use solely for requesting data from a service. I try to make my models as fat as possible and also do things like UI manipulation. Because I use JavaScriptMVC in combination with ASP.NET my usage of the View has been limited. I will be mainly speaking about the Models and Controllers given that my *.aspx (or *.ascx) page is my actual view.

When generating the application, JavaScriptMVC generated an index.html page for us which automatically includes the necessary javascript. We’ll be using that page to create our tab control. I changed the HTML a bit, this is the end result:

<html> <head></head> <script language="javascript" type="text/javascript" src="../../jmvc/include.js?tab_manager,development"></script> <style type="text/css"> li { border:solid 1px #000; float:left; margin-right:5px; padding:10px; } li.selected { background-color:#000; } li.selected a { color:#fff; } p, ul { margin:0; width:300px; } p { border:solid 1px #000; height:200px; padding:10px; } ul { list-style-type:none; height:41px; padding:0; width:300px; } </style> <body> <ul id="tab_manager"> <li class="selected"><a href="#tab-1">Tab 1</a></li> <li><a href="#tab-2">Tab 2</a></li> <li><a href="#tab-3">Tab 3</a></li> </ul> <p id="tab-1">Tab 1 selected</p> <p id="tab-2" style="display:none;">Tab 2 selected</p> <p id="tab-3" style="display:none;">Tab 3 selected</p> </body> </html>

Some basic CSS rules and HTML to represent the tabbed control. I think this code speaks for itself, let’s create the javascript that is needed to make this control work. In order to respond to events that occur we first need a controller that will do the event delegation. So we use the js.bat command batch to generate a controller, this is the command:

js jmvc/generate/controller tab_manager

In our controllers folder we now have a tab_manager_controller.js which we will use to hook into the events that occur on our html page. We can hook into different sort of events and all the important ones are supported. So in order to respond to a click on one of the tabs we add the following method to the controller.

TabManagerController = MVC.Controller.extend('tab_manager', /* @Static */ {}, /* @Prototype */ { 'li click': function(params) { new Tab().select(params.element); params.event.preventDefault(); } } );

Let’s reflect over this code. This code defines a TabManagerController class which controls an element that has the id ‘tab_manager’, the id that our ul-tag has. There is one function in the controller that will respond to a click event on a li-tag. Notice how they use CSS selectors to define the event handlers. No real rocket science here for anyone who already is familiar with JavaScript and CSS selectors. Now we could write all necessary code in the controller, but in order to keep my JavaScript code maintainable I would like to use the controllers as a delegation mechanism to my models. This is somewhat different from what the guys of JavaScriptMVC are recommending to use models for. I try to get my models to reflect the different concepts in my application. So we want to generate a Tab model to show and hide the different tabs. Again we use the batch we are already familiar with to generate the model:

js jmvc/generate/model ajax Tab

This will generate a Tab.js file under the models directory. Now we can add the necessary methods to this class in order to manipulate a certain tab. The nice thing about this is that it resides in its own class which has a name that reflects the actual UI object it will manipulate. You might have noticed the ‘ajax’ parameter we have provided. This is simply because a model has to be generated with a certain data type (json_p, ajax, xml_rest, …). We will not be using one of the types in our code so this can be ignored for now. This is how the implementation of the Tab model looks like:

/** * */ Tab = MVC.Model.Ajax.extend('Tab', /* @Static */ { }, /* @Prototype */ { getTabContentSelector: function($tab) { return $tab.find('a').attr('href'); }, hideTabContent: function(selector) { $(selector).css('display', 'none'); }, showTabContent: function(selector) { $(selector).css('display', 'block'); }, select: function(tabToShow) { var $currentSelectedTab = $('li.selected'); this.hideTabContent(this.getTabContentSelector($currentSelectedTab)); $currentSelectedTab.removeClass('selected'); var $tabToShow = $(tabToShow); $tabToShow.addClass('selected'); this.showTabContent(this.getTabContentSelector($tabToShow)); } } );

Notice that I use jQuery in the mix. jQuery and JavaScriptMVC are actually very compatible. You do have to include jQuery as a resource in order to be able to use it, more on that later. There are four functions in the Tab model, the most imporant one is the select function which takes a tab (which is actually an li-element) as argument. The content to show is currently hidden in the page, each tab has a corresponding p-tag which holds the data for that tab, but this could be replaced by an Ajax call in order to request some data for a specific tab.

The last thing I have to mention is the tab_manager.js file in the app folder. That file looks like this:

include.resources('jquery-1.3.2.min'); include.engines(); include.plugins( 'controller','controller/scaffold', 'view','view/helpers', 'dom/element', 'io/ajax', 'model/json_rest','model/xml_rest' ); include(function(){ //runs after prior includes are loaded include.models('Tab'); include.controllers('tab_manager'); include.views(); });

This is actually the main file of our application which determines what resources, plugins, models and controllers are loaded. As you can see the jquery file, the tab_manager controller and the Tab model are present in this file. Don’t forget to add new models and controllers to this file, otherwise they will not be loaded and executed.

Because this post is already rather lengthy I will leave it here and give you some room to expirement with the code. I know there are a lot of concepts that are left unexplained but I’m planning on writing a few more posts to go into further detail into certain concepts of the framework. As always if you have any remarks the comments are still open and free.

To finish would like to end with a brief overview  of the important files and folders so you have a nice overview of where to find everything we used in this tutorial.

apps tab_manager.js general application file to load models, controllers and resources index.html file which holds the necessary html code, used for testing purposes controllers tab_manager_controller.js controller which responds on events coming from the tab manager models Tab.js model which represent a tab resources jquery-1.3.2.min.js jquery file

Source code of this article
Result of this tutorial

Tags from Technorati: ,


Comments

July 7. 2009 12:35 PM

Trackback from WebDevVote.com

The MVC in JavaScriptMVC

WebDevVote.com

July 10. 2009 07:35 PM

Much better. Great post.
Reminds me a lot of grails/rails for the server side being able to create controllers and model. We are currently using extjs (extjs.com) as our javascript library and I really wish we would establish some type of MVC to improve maintainability. The unfortunate thing with this is now developers are having to duplicate effort twice on server and client size: grails create-model and js create-model. Sucks that we have to repeat this stuff in 2 places. The holy grail I think is define the model once and both can use it. I think flex might have this and maybe the new Ext.Direct that comes with the new version of extjs.

Also, I have some friends that are using Dojo and jquery on a project. Dojo I think is similar to JavascriptMVC, but I think it might have a larger community.

James Lorenzen

July 11. 2009 11:06 AM

Hey James,

Glad you liked the post. I did read about Dojo but thought it was just another JavaScript library (like jQuery, prototype, ...). I didn't know it had MVC capabilities I'll have to check it out one day.

I agree with you on the fact that we have to duplicate code on the server and on the client side. Although I don't have this problem very often because the client side is often more about interaction (changing tabs, animations, hiding elements) and the server side is more the 'real' business logic of the application.

I'm afraid the holy grail in this hasn't been found yet, let me know something if you've found it! Smile

Peter

July 12. 2009 02:00 PM

Pingback from pabloidz.wordpress.com

links for 2009-07-12 « pabloidz

pabloidz.wordpress.com

July 14. 2009 11:58 PM

@peter,
  Great article again!

However, I would strongly suggest against using Models for manipulating the DOM.  On a big project, it will be confusing.  All of that work should be done in a Controller.

If you must package your DOM manipulation, you can simply use a class:  MVC.Class.extend

However, as you use jQuery, you might be very excited about 2.0 which is coming this week!  It's a huge step forward, especially with testing.  Your controller might look like:

$.Controller.extend('TabManagerController',
{
   'ul li a click' : function(el, ev){
      ev.preventDefault()
      var oldid = this.find('li.selected').removeClass('selected').attr('href')
      this.find(oldid).hide();
      var newid = el.attr('href')
      this.find(newid).show();
      this.el.parent().addClass('selected')
   },
   'ul li mouseover' : function(el){
      el.addClass('over')
   },
   'ul li mouseout' : function(el){
      el.removeClass('over')
   }
})

This assumes your HTML looks more like

<div class='place_for_tabs'>
   <ul> .... </ul>
   <p id="tab-1"> ..... </p>
   <p id="tab-1"> ..... </p>
   <p id="tab-1"> ..... </p>
</div>

You attach this functionality to your tabs like:

$('. place_for_tabs').tab_manager_controller()

I added the mouseover / out functionality to hint at the benefits of grouping.

Let me know if you have any questions / comments.  Hopefully this looks right when I press submit.

  

Justin Meyer

July 15. 2009 12:04 AM

One more minor suggestion, it's common to 'bottom' load JavaScriptMVC applications.  By this I mean, put the script tag right before the closing </body> tag.  This allows the browser to put up some content for the user to see before the JavaScript payload is delivered.  Although the time to 'onload' is about the same, it feels faster for a user.

Justin Meyer

July 16. 2009 08:23 AM

Pingback from jaceju.net

網站製作學習誌 » [Web] 連結分享

jaceju.net

July 17. 2009 12:33 PM

@Justin

You're right about bottom loading the javascript, I'll have to update my starting html template.

I'm not sure I agree with you on putting DOM manipulation in the controller, on my current project I've started out putting it all there but it became messy very quickly. I tried out to put this in a model and I have the feeling it's more organized. It'll probably feel the same if I put it in a class like you mentioned. Where would those classes reside in the JavaScriptMVC structure? In the resources folder?

Regarding the JavaScriptMVC 2.0 release, I absolutely LOVE the idea that you use jquery to hook a controller onto a piece of HTML. But where will you provide the hook? Is there going to be some kind of initializer file to put the on load code to link the controller to the html part?

Thanks for the great feedback!

Peter

July 18. 2009 02:36 PM

Trackback from Peter works on the web!

Compressing and combining files with JavaScriptMVC

Peter works on the web!

Add comment




  Country flag

biuquote
Loading