The web users of today expect a modern experience that only a single page application can deliver. Page refreshes are a thing of the past. Waiting for anything longer than 2 seconds without a “cool effect” is not acceptable. Responsive design for tablets and smartphones should just work.
By the way, I have tried Backbone in production and let me tell you that CanJS is the underdog. It gives you more bang for your buck while still staying out of your way. Also, having no controllers in Backbone is very awkward. Let’s see how to build a modern website in an HTML5 world!
Also a note on CanJS’s AMD support. CanJS released an AMD version of their library starting from 1.1, but I purposely did not use it. The implementation is still young and I have encountered some subtle issues with it. In addition, I don’t like how the implementation ruins the folder structure by forcing you to place the CanJS folder with main.js (instead of in the libs folder). It seems there isn’t a RequireJS setting to fix this either. The library is so small though and I use many parts of the framework anyways, I figure it’s fine to load can.jquery.min.js via shim until CanJS’s AMD support matures a bit more.
For illustration, the complete single page app structure should look something like below. Notice I am storing all 3rd party libraries and frameworks in the /libs folder. This does not necessarily mean I will be loading them all, but just on-demand via AMD.
Now comes the fun part – setting up the MVC router! This is the centerpiece for any MVC application. The router will get initialized in main.js. Let’s take at main.js, which is essentially used to configure require.js and start your app:
A lot of interesting things are happening in the router. First is the route patterns. There is one for the home page, a couple static pages, and some dynamic ones. The last one with controllers and actions is a powerful one. This will “dispatch” the request to the right controller and action simply by interpreting the current route. This is how a real MVC application should work. A dynamic route that makes dynamic requests to your application! For example, going to #products/detail/7 will call your “Products” controller and then execute the “detail” function with “7” as the parameter passed to it. See? Single page applications can be much more than jQuery scrolls between page sections 🙂
Most of the heavy lifting is happening the in the base class in “utils/basecontrol“. All of our controllers will be inheriting from this so they automatically inherit functionalities like launching a modal window or loading different pages. Notice in the init, the base class init is being called with the snazzy Construct.super plugin for CanJS. Next, events are being subscribed to, such as button clicks. The last part of the control are all the actions that are usually called by the router or from other controller actions.
Keep all your templates in the views folder. These are HTML files that are asynchronously called from your controller actions. Views can be static or can use a tempting engine like Mustache to bind data to. CanJS has recently added support for Mustache and works beautifully. After the view has been loaded asynchronously in the action, the callback is available for you to perform extra work on the template elements, such as creating a Kendo grid or initializing a jQuery plugin on an element.
See it in action!
You can view the demo in a separate page here. There are no page refreshes since all the page templates are being asynchronously loaded. All the links on my page are hash URL’s. So instead of going to “/contact.html“, you would go to “#contact” and the router would listen to this. Then the user will be dispatched to the right controller, action, and view! The code is hosted at GitHub so please feel free to fork and contribute.
Single page applications are a reality and not just for hobbyists anymore. Full-fledged enterprise applications are being run on these architectures as we speak. With Silverlight and Flash dying a slow death, HTML5 apps are leading the way to the promise land!
Latest posts by Falafel Posts (see all)
- Matching Complex Query String Rewrite Rule in IIS - March 22, 2017
- Using Google Services in UWP C# Apps – Part 2 - February 7, 2017
- Using Google Services in UWP C# Apps – Part 1 - February 6, 2017
- Redis Caching in the Google Cloud Platform - February 3, 2017
- Entity Framework with Google Cloud SQL - February 2, 2017