Build Single Page Apps - Part 6 - JavaScript Modules and Italian Food
SPA’s are apps too. And as such when creating a SPA you will find yourself writing code to get data, push data, handle validation, change tracking, storing data locally, presenting data, and so much more. All of these roles could be written in a scattered manner across the app, but that causes redundancy, low code re-use and a readability and maintenance headache. And for you pattern folks (like me) it also violates DRY and SRP.
So what do we do? Make more, smaller code files that each handle one specific job.
You can get the details on how I use this approach in my upcoming Pluralsight course titled “Building Single Page Apps (SPA) with HTML5, ASP.NET Web API, Knockout and jQuery”. It’s T-minus 2 weeks before it’s due to be completed … so it’s getting close!
You can catch up on the previous posts in this series here:
More on the Code Camper SPA
Part 1 - The Story Begins (What is the Code Camper SPA?)
Part 2 - Client Technologies
Part 3 - Server Technologies (the Data Layer)
Part 4 - Serving JSON with ASP.NET Web API
Part 5 - HTML 5 and ASP.NET Web Optimization
Part 6 - JavaScript Modules
Part 7 - MVVM and KnockoutJS
Part 8 - Data Services on the Client
Part 9 - Navigation, Transitions, Storage and Messaging
Part 10 - Saving, Change Tracking, and Commanding
Part 11 - Responsive Design and Mobility
Spaghetti to Ravioli
I get ideas that pop in my head quickly and then they often leave just as quickly. So it’s important for me to get them written in code before I lose that thought. Because of this, I write code in iterations. I write it, test it, refactor it, test it, refactor it, and so on.
Pass 1: Toss the Spaghetti Against the Wall
The first pass of my code is rarely the best or most elegant, but it gets the job done. If I stopped here, I’d leave a code smell behind and have a lot of intermingled code that nobody would enjoy maintaining and would be impossible to test or expand upon.
Pass 2: Tasting the Spaghetti
Then I refine the code for all use cases. This is where I make sure it handles the roles that the code needs to handle. It’s better code, but still has a lot of duplication and ugliness.
Pass 3: Introducing Ravioli
Finally, I refactor it to be more maintainable. This is where I optimize the code, shorten it, create extension methods, service classes, and apply DRY and SRP.
Spaghetti Example
Let’s look at an extremely oversimplified and crude example. Below is an example of JavaScript code that displays a message. We could use this code throughout our app to display messages. However it explicitly hard codes the message to display, hard codes the jquery to do it, and if you wanted to change how it works you’d have to search all of your code to find it and replace it. Not very reusable and likely to have typos.
$('#messagebox').text('Welcome to Code Camp, John');
We could wrap this in a function, and that would help. But then what if other files want to use it? Then we need to make sure it is loaded in time. We could put it in its own module, and then it would be more reusable. Then we could make it use a custom message, but maybe a better solution is to tell it how to get the message. For that we could tell it to go talk to a module to get the message.
Again, this is an overly simplified example … but it is easily applicable to larger code issues.
Ravioli Example
Below is an example of a JavaScript module called alerter. It’s job is to display a message. It doesn’t know what message it will display, because that’s not its job. For that it depend upon jQuery and another module called a dataservice. Notice that the module uses a define statement to identify:
- The ID of the module
- The dependencies
- The factory for the module
- This ‘alerter’ module can be loaded as needed (using RequireJS)
- It is self-sufficient since it defines the dependencies up top (jQuery and dataservice). RequireJS will go out and find those modules and load them first, before the alerter is cranked up. How nice!
- The alerter doesn’t know how to go get the message, it defers that to the dataservice. So alert just “alerts” the user … which is its job.
- Using this code all over our app is as simple as saying
- Defines modules
- Resolve module dependencies
- Load scripts in the proper order (and asynchronously)
This code has a few advantages our first spaghetti example did not:
alerter.showMessage()
RequireJS
In my course I use RequireJS to handle the dependency resolution for my modules. RequireJS helps:
It does much more, too … but the reason I like using RequireJS is that it helps define a structure to the modules in my JavaScript apps that I otherwise would not have.
Want More?
In my course I go deeper on this concept and show how modularity can make it much easier to manage a SPA as it grows. I include several examples and as a bonus, here is a quick introductory sample using modularity and RequireJS on github that I wrote to help explain it. I call it kis-requirejs-demo for “Keep It Simple RequireJS Demo”
It’s part of a Visual Studio 2012 RTM solution, so you will need that to run it as is. Or you can pull the HTML and JavaScript out yourself and use another tool.