Build a simple client-side MVC app with RequireJS

Require JS Logo

This article was written in 2011. Because the web changes quite fast, some information here might be outdated.

As a web developer, you certainly often started coding your JavaScript in a single file, and, as the code base gets larger and larger, it became really difficult to maintain. To solve this problem you can split your code in several files, add more script tags and use global variables to reach functions declared in other files. But this pollutes the global namespace and for each file an additional HTTP request consumes bandwidth, which slows down the loading time of the page.

If this happened to you, you certainly understand that there is a strong need to organize our front-end code differently, particularly if we have to build large-scale web apps with thousands of lines of JavaScript. We need a new way to organize all this mess to make it easier to maintain. This new technique consists in using script loaders. Plenty of them are available on the web, but we'll focus on a very good one called RequireJS.

In this step by step tutorial you will learn how to build a simple MVC (Model - View - Controller) app using RequireJS. You don't need any particular previous knowledge of script loading - we'll see the basics together.

Introduction

What is RequireJS and why it's awesome

RequireJS is an implementation of AMD (Asynchronous Module Definition), an API for declaring modules and loading them asynchronously on the fly when they're needed. It's developed by James Burke, and it just reach the symbolic 1.0 version after 2 years of development. RequireJS can help you organize your code with modules and will manage for you the asynchronous and parallel downloads of your files. Since scripts are loaded only when needed and in parallel, it reduces the loading time of your page, which is great!

MVC for the front-end?

MVC is a well known design pattern to organize server-side code and make it modular and sustainable. What about using it for front-end? Can we apply this design pattern to JavaScript? If you're just using JavaScript for animating, validating few forms or any simple treatment that doesn't require many lines of code (let's say less than 100 lines), there is no need to structure your files using MVC, and probably no need to use RequireJS either. However if you're building a rich web app with many different views, definitely yes!

The app we'll create

To illustrate how to organize your MVC code using RequireJS, we'll create a very simple app with 2 views: This is how it will look like at the end: Require JS MVC App The business logic is obviously super simple so you can focus on understanding what's really important here: structuring your code. And since it's that simple, I strongly recommend that you really try to do it in parallel of reading this tutorial. It won't take long, and if you have never done modular programming or used RequireJS before, coding this example will really help you become a better programmer. Seriously, it's worth it, you should do it.

HTML and CSS files

Here is the HTML markup we'll use for this example: The navigation in our app will be the links of the nav menu which will remain present on each page of the app, and all the magic of the MVC application will happen in the #app div. We also included RequireJS (which you can grab here) at the bottom of the body, and you can notice a special attribute on the script tag: data-main="js/main". The value passed to this attribute is used by RequireJS as the entry point of the entire application.

Let's also add just a little bit of basic styling:

OOP reminder: What is a module?

In JavaScript Object-Oriented Programming, there is a very common design pattern called Module Pattern. It is used to encapsulate methods and attributes in objects (which are the "modules") to avoid polluting the global namespace. It is also used to kind of simulate classes from other OOP languages like Java or PHP. This is how you would define a simple MyMath module in our main.js file: Public methods are declared using the object literal notation, which is not very convenient. You can alternatively use the Revealing Module Pattern, which returns private attributes and methods: I will be using the Revealing Module Pattern in the rest of this article.

RequireJS

Defining a module with RequireJS

In the last section we declared a module in a variable to call it later. This is just one way to declare modules. We're now going to see a (barely!) different method used by RequireJS. The purpose of RequireJS is to split our JavaScript files for a better maintainability, so let's create a MyMath.js file to define our MyMath module in the same folder as main.js: Instead of declaring a variable, we just put the module as a parameter of the define function. This function is provided by RequireJS and will make our module accessible from the outside.

Requiring a module from the main file

Let's go back to our main.js file. RequireJS provides a second function called require that we're going to use to call our MyMath module. Here is what our main.js now looks like: The call to MyMath is now wrapped in the require function, which takes 2 parameters: You can now reload the page and... congratulations! You just called a method from an other file! Yes, that was super easy and you're now ready for the big scary MVC architecture (which works exactly like the module definition you just did, so be sure you'll be doing great!).

The MVC structure

Important note: In this tutorial, we'll mimic the server-side MVC in which 1 controller = 1 view. In front-end development, it is very common to have multiple views per controller. In this case views become visual components like buttons or fields. MVC JavaScript frameworks like Backbone use this different approach which is not the purpose of this article. My goal here is not to create a real life entire MVC framework, but simply illustrate how RequireJS works through a structure that many of you already know well.

Let's start with the part I'm sure you love: creating all the folders and files of our project. We want to use Models to represent data, the business logic will be handled by Controllers, and those controllers will call specific Views to render pages. So guess what? We need 3 folders: Models, Controllers and Views. Considering our simple app case, we'll have 2 controllers, 2 views and 1 model.

Our JavaScript folder now looks like this: Got the structure ready? Great! Let's start implementing the simplest part: the Model.

The Model: User.js

In this example, a User will be a simple class with a name attribute: If we now come back to our main.js file, we can declare the dependency to User in the require method, and manually create a set of users for the purpose of this example: We then serialize in JSON the users array and store it in the HTML5 local storage to make it accessible just like a database: Chrome Console showing the users

Note: JSON serialization with stringify and deserialization with parse need a polyfill to work in IE7 and inferiors. You should use Douglas Crockford's json2.js from his Github repository for this.

Displaying the users list

It's time to display those users in the app interface! We'll have to work with ListController.js and ListView.js to do that. Those 2 components are obviously related, and they'll be linked somehow. There are many ways we can do that, and to keep this example simple, here is what I suggest: The ListView will have a render method and our ListController will simply get the users from the local storage and call ListView's render method by passing the users as a parameter. So obviously, ListController needs ListView as a dependency.

Just like with require, you can pass an array of dependencies to define if the module relies on other modules. Let's also create a start method (or any other name that makes sense to you - like run or main), to put the main behavior of the controller in it: Here we deserialize the users from the local storage and pass it to render as an object. Now, all we have to do is implementing a render method in ListView.js: This method simply loops on our users to concatenate them in an HTML string we inject in the #app element.

Important: Using plain HTML in a JavaScript file like this is not an ideal solution, because it's very hard to maintain. You should instead consider templating. Templates are an elegant way to insert data in HTML markup. Many very good templating systems are available on the web. You can for instance use jQuery-tmpl or Mustache.js. But this is beyond the scope of this article and it would add complexity to the current architecture, so I prefer keeping it simple.

Now, all we need to do is to "run" our ListController module. To do that let's declare it as a dependency of require in our main.js file, and call ListController.start(): You can now refresh your page to get this wonderful list of users: MVC users list Woooaaaah, it works! Congratulations if you coded this too!

Note: For the moment, we can only manually declare the controller we want to run since we don't have any routing system yet. But we'll create a very simple one pretty soon, be patient!

Adding a user

We now want to be able to add users to our list. We'll display a simple text input and a button, with an event handler when the button is clicked to add the user to the local storage. Let's start with AddController, just like we did in the previous section. This file will be pretty simple since we don't have any parameter to give to the view. Here is AddController.js: And its corresponding view: You can now declare AddController as a dependency in your main file and call its start method to successfully get the expected view: MVC Add user But since we don't have any event bind on the button yet, this view is not very useful... Let's work on that. I have a question for you: Where should we put the event logic for this event? In the view? In the controller? If we put it in the view it would be the right place to add the events listeners, but putting the business logic in a view would be a very bad practice. Putting the event logic in the controller seems to be a better idea, even if it's not perfect because we don't want to see any div's ID in there, which belong to the view.

Note: The best way to go would be having event listeners in the view, which would call business logic methods located in the controller or in a new dedicated events module. This is really easy to do but it would make this example more complex and I don't want you to be lost. Feel free to try doing it for practicing!

As I said, let's put all the event logic in the controller. We can create a bindEvents function in AddController and call it after the view has finished rendering the HTML: In bindEvents, we simply add an event listener for clicks on the #add button (feel free to use your own function to deal with IE's attachEvent - or just use jQuery). When the button is clicked, we get the users string from the local storage, deserialize it to get the array, push a new user with the name contained in the #user-name input field, and put the updated users array back to the local storage. After that we finally require the ListController to execute its start method so we can see the result: MVC List - User added Brilliant! It's time for you to take a break, you've done a very good job if you're still doing this example with me. You deserve a cup of coffee before we continue!

Navigation between views with routes

Okay back to work. Our little app is pretty cool but it really sucks that we still cannot navigate between views to add more users. What is missing is a routing system. If you've worked with server-side MVC frameworks before, you're probably familiar with this. Each URL leads to a different view. However here we're client-side and it's slightly different. JavaScript single page interfaces like this one use the hash of the URL to navigate between different parts of the app. In our case, we want to be able to reach the 2 different views when hitting those URLs: This will make each page of our app bookmarkable and easily reachable.

Note: Firefox, Chrome and Opera also have a pretty good support of HTML5 history management (pushState, popState, replaceState), which saves you from dealing with hashes.

Browsers compatibility and functioning

Managing history and hash navigation can be very painful if you need a good compatibility with old browsers. Depending on the browsers you aim to support here are different solution you can consider: In our case we will do the simple manual monitoring, which is pretty easy to implement. All we need to do is checking if the hash changed every n milliseconds, and fire some function if a change is detected.

Note: A jQuery plugin is also available to manage this for you.

The routes and the main routing loop

Let's create a Router.js file next to main.js to manage the routing logic. In this file we need a way to declare our routes and the default one if none is specified in the URL. We can for instance use a simple array of route objects that contain a hash and the corresponding controller we want to load. We also need a defaultRoute if no hash is present in the URL: When startRouting will be called, it will set the default hash value in the URL, and will start a repetition of calls to hashCheck that we haven't implemented yet. The currentHash variable will be used to store the current value of the hash if a change is detected.

Check for hash changes

And here is hashCheck, the function called every 100 milliseconds: hashCheck simply checks if the hash has changed compared to the currentHash, and if it matches one of the routes, calls loadController with the corresponding controller name.

Loading the right controller

Finally, loadController just performs a call to require to load the controller's module and execute its start function: So the final Router.js file looks like this:

Using the new routing system

Now all we have to do is to require this module from our main file and call startRouting: If we want to navigate in our app from a controller to another, we can just replace the current window.hash with the new controller's hash route. In our case we still manually load the ListController in AddController instead of using our new routing system: Let's replace those 3 lines with a simple hash update: And this is it! Our app has now a functional routing system! You can navigate from a view to another, come back, do whatever you want with the hash in the URL, it will keep loading the right controller if it matches the routes declared in the router. Sweet!

Conclusion

You can be proud of yourself, you built an entire MVC app without any framework! We just used RequireJS to link our files together, which is really the only mandatory tool to build something modular. So now what are the next steps? Well if you liked the minimalist Do It Yourself approach I had in this tutorial, you can enrich our little framework with new features as your app grows and has new technical needs. Here are some ideas of potential interesting next steps: This DIY approach is excellent to learn, but the current state of our framework is really not sufficient for real life projects at this point. If you're too lazy to implement the features listed above (and this list is absolutely not exhaustive!), you can also start learning how to use an existing MVC framework. The most popular are: I personally like Backbone because it's lightweight (less than 5kb minified), so my next tutorial will be about mixing RequireJS and Backbone, which works pretty well! Follow @verekia for updates about this. You should also follow Addy Osmani and read his tutorials about large scale JavaScript applications and modular JavaScript, and James Burke the creator of RequireJS. Both are very good sources of information for building modular JavaScript apps. Addy Osmani also started a project called TodoMVC to compare how different MVC frameworks can be used to build the same simple web app, which is really helpful to choose the right framework for your needs.

That's all for today folks, thanks for reading!

Posted on