In my bachelor project, an online portfolio for our university, we used the JavaScriptMVC framework to facilitate the implementation of various JavaScript widgets, e.g. image cropping, file upload, file sorting, project filtering, ui enhancements etc. You can read more about this project in this article.
Usually JavaScript MVC Frameworks are used to build desktop like single page applications. Single page applications are referred to as websites where all the content is available without reloading the page – every content is pulled in by AJAX and modifications to data are sent back to the server the same way. This is where models come in handy. But in our case we have a normal website with many different pages. We only use JavaScript to sort some lists or to have a interactive filter menu. In this article i will explain how we used the JavaScriptMVC Framework to realize this.
First of all, JavaScriptMVC is based on jQuery. This means it only works with jQuery. The advantage is, it is integrated in it’s functionalities. You’ll see why this is handy. It also ships with a integrated testsuite where one can write unittests etc. and an dependency solver called Steal. Steal can be used to require other JavaScript files which helps to easily organize your JavaScript code into as many files as you like. These files can later be compiled into one production file using the google closure compiler.
Because we have a multi page application, we only need parts of the framework, which is not a problem, because we can select only the parts of the framework that we really use. Only these parts are compiled into the production code, which is great
We decided to use the controllers for our widgets. Such a controller can be mounted on a DOM element and then control everything that happens inside that element and its children. The following code shows a simple example of such a controller. (The JavaScriptMVC documentation can be found here)
$.Controller("Portfolio.Project", {
init : function(el, options) {
// do some stuff on initialisation
},
// this gets called when the child element with the id "read_more_link" is clicked
"#read_more_link click": function(el, ev) {
ev.preventDefault();
// this.element is the element this controller is mounted on
this.element.find(".long_description").show();
this.element.find(".short_description").hide();
}
});
The controller can then be mounted on a HTML element and it’s init() function gets called:
$("#project").portfolio_project();
This way widgets can be programmed easily
To require this controller on application load, we require it in our main JavaScript file (portfolio.js) using Steal:
steal('../jquery/jquery')
.plugins('jquery/dom/cookie','jquery/controller','portfolio/project', 'portfolio/other_widtgets...'');
Using the steal() function, you can require any JavaScript file you like. plugins() loads our widgets and parts of the framework like cookie support and the controller base class. In order for our widget to be found by Steal, it has to be in the right folders and follow the naming conventions for plugins. We used the following folder structure:
- public
|-javascripts
|-jquery << framework code
|-steal << steal code
|-portfolio
|-profile
| |- profile.js << profile widget
|- portfolio.js << main File
This way the file profile.js in the profile folder is found by steal. To load the main file, the following code has to be included in the application layout:
Production mode: <script type='text/javascript' src='/javascripts/steal/steal.production.js?portfolio'></script> Development mode: <script type='text/javascript' src='/javascripts/steal/steal.js?portfolio,development'></script>
In development mode all required files are synchronously loaded by Ajax, in production mode only one compressed JavaScript file is loaded.
Additional Steal requires can be put in any file you want. For example in a controller that needs JavaScript Views:
steal.plugins('jquery/view/ejs', 'jquery/view').then(function() {
$.Controller("Portfolio.Person", {
init : function(el, options) {
...
},
addPersonRow: function(json) {
this.element.find(".persons").append("person_template", json);
}
});
});
First ‘jquery/view/ejs’ and ‘jquery/view’ are required by Steal and then, when these dependencies are loaded, the controller code inside the then() function is executed.
The addPersonRow() function in this example uses a view to generate HTML and appends it to the DOM. As you can see views are directly built into the jQuery append function (and others) which is very handy. A View is a piece of HTML code, that is included in the HTML that is generated by the server side application. This way styling and the HTML stucture are seperated from JavaScript, which is really good. The append() function now gets passed two parameters: The template id and a object containing the values that are used in the view. <%=something%> prints a value whereas any javascript code can be included in views when placed between <% (javascript here) %>:
<script id="person_template" type="text/ejs">
<tr class="person" id="sort_<%=person.id%>">
<td>
...
</td>
<td>
Hello <%=person.name%>
</td>
</tr>
</script>
So $(”#some_element”).append(”person_template”, { person: { id: 1, name: ‘Jack’ } }); will generate the following HTML and append it to #some_element:
<tr class="person" id="sort_1">
<td>
...
</td>
<td>
Hello Jack
</td>
</tr>
I hope this gave you a quick overview on how JavaScriptMVC can be used on a multi page apllication