Probably one of the most difficult tasks a web developer faces is creating a code base that can grow gracefully as the project grows. The larger and more complicated a project becomes, the more difficult the task. Shaping a project’s code base in a way that makes troubleshooting, maintenance, and enhancements easier, not harder, is no small feat, though. This is true for even traditional web projects.
In an SPA, keeping your code segregated based on its functionality is more than just a good practice. It’s critical to being able to successfully implement an application as a single page. The key to this is creating a separation of concerns within your application. Having a separation of concerns within your code base means that you’re making a concerted effort to separate the various aspects of the application’s code based on the responsibility it has.
We can break the overall SPA into many application layers, on the server side as well as in the client. Within the browser space, we can begin our quest for creating a separation of concerns in a fundamental way by first remembering the roles of our three primary languages: HTML, CSS, and JavaScript:
We’ve all worked with these three languages and understand the role of each. Even so, because these three languages can easily be mixed together, your code can quickly turn into spaghetti (see figure 2.1). This can make your project extremely challenging to manage.
It’s possible, however, to produce code written in a decoupled manner but still achieve the same level of interactivity between each layer. Each part of your code can be compartmentalized based on the purpose it serves.
Additionally, this compartmentalization can be extended to encompass the application’s data versus its presentation to the user. Data and events can be assigned to your UI via mapping, instead of direct assignment in your business logic. The UI and the data can both be observed for changes, allowing them to stay in sync and giving your logic a means to react appropriately. So not only can the code itself be segregated based on its responsibility, but your UI’s presentation and the data it represents can be disjoined. This achieves yet another level of separation in your application (see figure 2.2).
Although it’s entirely possible for you to create a homemade solution to manage all these layers of separation, it’s probably not where you want to spend your development time. Thankfully, though, a myriad of libraries and frameworks are ideal for just such a task. If used in your application, they can play a key role in creating a successful separation of concerns by externally managing the relationship between your logic, data, and the UI. In varying degrees, they also provide many other features to assist you in building your SPA.
This chapter defines JavaScript MV*, briefly discusses its evolution from traditional UI design patterns, and discusses what these frameworks do for us. The chapter also breaks down some common MV* concepts, using three MV* frameworks to illustrate different approaches to the same objective. As mentioned in chapter 1, not everything is MV*. My focus in this book, however, is on the MV* style of frameworks.
To further demonstrate how the same concepts are prevalent in MV*, even though the approach may differ, you’ll create a small but reasonably realistic online directory project with each framework. I’ll abbreviate the code samples in the text so the concepts don’t get lost in the code. (The code for all three versions is available in appendix A, with a complete code walk-through. And the code for each is available for download online.)
The end of the chapter includes a list of things to consider when selecting a framework that’s right for you. Because all MV* implementations are different approaches to the same problem, you’ll ultimately have to decide which one is the right fit for you. There’s no clear right or wrong answer that fits all situations. Once you understand the role of MV* and its underlying patterns, though, you’ll be able to select one that best fits your environment. After all, no one knows your situation better than you do. You know the factors that affect your project, your end users, your budget, your timelines, and your development resources.
The term MV* represents a family of browser-based frameworks that provide support for achieving a separation of concerns in your application’s code base, as discussed in the introduction. These frameworks have their roots in traditional UI design patterns, but the degree to which they follow a pattern varies from implementation to implementation.
In MV*, the M stands for model and the V stands for view. Section 2.2 covers them in depth, but for this discussion let’s briefly summarize each term:
As you’ll see in section 2.1.1, traditional UI design patterns include a third component, which helps manage the relationship between the model and the view, as well their relationship with the application’s user. Although most modern browser-based UI design frameworks based on MVC/MVVM have some notion of a model and a view, it’s the third component that varies, in both name and the duty it performs. Therefore, people have generally settled on the wildcard (*) to represent whatever the third component might be.
Section 2.1.2 presents a lot more about MV* in the browser. First, though, let’s find out a little about traditional UI design patterns, which form the roots of MV*. Knowing how we got here will help you get a better idea of why things work the way they do.
Using architectural patterns to separate data, logic, and the resulting representation of the output is a notion that has been around for a long, long time. Central to these design patterns is the idea that an application’s code is easier to design, develop, and maintain if it’s segmented based on the type of responsibility each layer has.
This section details the three patterns that have had the most influence on client-side approaches: Model-View-Controller (MVC), Model-View-Presenter (MVP), and Model-View-ViewModel (MVVM). After a proper introduction to these design patterns, you’ll see in section 2.1.2 how they relate to the MV* frameworks we see in the browser today.
Model-View-Controller (MVC) is one of the oldest patterns to try to separate data, logic, and presentation. MVC was proposed by Trygve Reenskaug and later implemented in the Smalltalk programming language in the 1970s.
MVC was instrumental in the design of graphical user interfaces then and still is today. Since its inception, it and its variants have become common design patterns for all types of software development. The MVC pattern includes the model, the view, and a controller (see figure 2.3):
The interactions with the controller set off a chain of events that eventually lead to an update of the view. The view is aware of the model in this pattern and is updated when changes are observed.
In 1996, a subsidiary of IBM called Taligent came up with a variation of MVC called Model-View-Presenter, or MVP. The idea behind this pattern was to further decouple the model from the other two components of MVC. Under MVP, a controller-like object and the view would jointly represent the user interface, or presentation. The model would continue to represent data management. As noted in figure 2.4, in MVP, there’s no controller acting as a gatekeeper. Each view is backed by a component called a presenter:
The presenter takes on the task of keeping the view and model updated. Having an object in the middle allows the view and model to have more-focused responsibilities.
Model-View-ViewModel (MVVM) was proposed by John Gossman in 2005 as a way to simplify and standardize the process of creating user interfaces for use with Microsoft’s Windows Presentation Foundation (WPF). It’s another design pattern that emerged to try to organize the code associated with the UI into something sensible and manageable, while still keeping the various components of the process separate.
As in MVP, the view itself is the point of entry. Also like MVP, this model has an object that sits between the model and the view (see figure 2.5). The third component in this pattern is called the ViewModel:
Now that you know a little about traditional UI design patterns, you can better understand browser-side MV* approaches. Let’s fast-forward, then, and talk about the MV* we see in the browser.
Just like application code running on the server or natively as a desktop application, code running in the browser can benefit from using good architectural design patterns. In recent years, many frameworks have cropped up, aiming to fulfill this need. Most are based on MVC, MVP, or MVVM to some degree. The browser is a different sort of environment, though, and we’re dealing with three languages at once (JavaScript, HTML, and CSS). Therefore, it’s difficult to perfectly match a browser-side MV* framework with a design pattern. Trying to pigeonhole them into one category or another is, in most cases, a fruitless undertaking. Design patterns should be malleable strategies, not inflexible directives.
One of the reasons why the term MV* sprang up in the first place is that it’s often hard to nail down what the third concept in the framework is. The term represents sort of a compromise, to cease the endless disputes about whether particular frameworks are more this pattern or more that pattern.
The remnants of the traditional patterns are there but are loosely interpreted. Each one has some form of the data model, whether it’s in the form of a POJO (plain old JavaScript object) or some model structure dictated by the implementation. Each also has some notion of a view. The third cog in the machine might be a little more elusive, though. The framework might employ an explicit controller, presenter, or ViewModel. But it might have some sort of hybrid or not have the third part at all!
Derick Bailey, creator of Marionette.js for Backbone.js, put things rather eloquently in one of his online posts, titled “Backbone.js Is Not an MVC Framework”:
Ultimately, trying to cram Backbone into a pattern language that doesn’t fit is a bad idea. We end up in useless arguments and overblown, wordy blog posts like this one, because no one can seem to agree on which cookie-cutter pattern name something fits into. Backbone, in my opinion, is not MVC. It’s also not MVP, nor is it MVVM (like Knockout.js) or any other specific, well-known name. It takes bits and pieces from different flavors of the MV* family and it creates a very flexible library of tools that we can use to create amazing websites. So, I say we toss MVC/MVP/MVVM out the window and just call it part of the MV* family.
Source: http://lostechies.com/derickbailey/2011/12/23/backbone-js-is-not-an-mvc-framework/
Many other people share this same viewpoint about the fruitlessness of trying to one-for-one match today’s MV* with a traditional design pattern. The idea of the usefulness of the framework taking priority over its categorization gained even more steam when the AngularJS team weighed in with a similar conclusion about their framework. Igor Minar (from the AngularJS team) famously blogged that developers will argue endlessly about how to categorize a particular MV* framework. He went on to state that AngularJS started out more like MVC, but over time it has become a little more like MVVM. In truth, it’s a little like both. In this same blog entry, he proposes the term MVW, which has since stuck:
I’d rather see developers build kick-ass apps that are well-designed and follow separation of concerns than see them waste time arguing about MV* nonsense. And for this reason, I hereby declare AngularJS to be MVW framework—Model-View-Whatever. Where Whatever stands for whatever works for you.
Source: https://plus.google.com/+IgorMinar/posts/DRUAkZmXjNV
Knowing that most MV* implementations only loosely base their design on the original pattern helps us remember that it’s not so important to try to brand the framework as one pattern or another.
Now that you know what MV* is, let’s go over a few common concepts that are frequently found, no matter the implementation. In the examples for each concept, you’ll quickly begin to see that even though the syntax and approach may vary between frameworks, the ideas are the same. Before we begin, let’s take a moment to review at a high level the concepts covered in this section:
Figure 2.6 gives a big-picture view of how these concepts relate to each other in an SPA. These concepts are probably the least common denominator in building SPAs. Other features, such as routing (covered in chapter 4), are also common (and necessary) but may not be provided universally. Not to worry, though. I cover many of the other concepts later in the book. We just need a sound foundation to begin with.
Because we’re using three frameworks for illustration in this section, some introductions are in order. Each represents a slightly different approach to these basic MV* concepts. Seeing the different approaches, though, should give you a broader perspective ultimately. The three frameworks are as follows:
Description: As mentioned before, Backbone.js doesn’t perfectly fit a traditional design pattern but could be described as being somewhere between MVC and MVP. Backbone.js is code driven. Models and views are created programmatically, using JavaScript code in this framework, by extending Backbone.js objects. By extending core objects, you automatically inherit a lot of built-in functionality. The framework also provides other out-of-the-box features to make routine tasks easier. Backbone.js doesn’t provide everything you’ll need in your SPA, though, so you must fill certain gaps using other libraries or frameworks.
Description: Knockout may not perfectly fit with the original MVVM definition, but it’s fairly close. In this framework, the model is any source of data, not an explicit object structure prescribed by the framework. Views and templates are created with plain HTML. The ViewModels that map model data to UI elements and provide views with behavior are created programmatically using JavaScript code, but most everything else is done declaratively by adding custom attributes to the HTML. Knockout is mainly concerned with making the binding process clean and easy. Though this makes the framework small and superbly focused, it leaves you to look to other frameworks and libraries for all other SPA requirements.
Description: AngularJS humbly describes itself as the “Superheroic JavaScript MVW Framework.” The creators of AngularJS designed it to be a one-stop-shopping kind of framework. Most, if not all, of your SPA needs are covered by this framework. AngularJS mixes and matches concepts that its creators liked from traditional patterns, as well as from other popular frameworks, to come up with a nicely balanced palette of out-of-the-box features. Part of your work in this framework will be done programmatically via Java-Script code, and part will be done declaratively using custom HTML attributes.
To help illustrate our list of common concepts, you’ll create a simple online employee directory. You’ll create it three ways, using each of the frameworks previously described. Later in the book, you’ll learn about more-advanced topics, such as routing and server transactions. For now, you’ll stick to the basics for this project. Our example, though somewhat contrived, still covers basic CRUD operations over a list. That should be sufficiently challenging for an introduction.
Let’s go over our objectives for this example:
Now that you’ve reviewed the objectives, take a look at the screenshot of the final product (see figure 2.7). The application will look and behave the same for each MV* framework used.
Throughout our discussion of MV* concepts, I’ll refer to this example. I’ll also talk about how different philosophies by the framework creators affect the type of code you’ll create. Although only certain parts of the code will be used for illustration, all of the code for each MV* version is in appendix A and available for download.
You know from our discussion of patterns that models often contain business logic and validation. They also represent the data in your application. The data they contain shouldn’t be a motley crew of unrelated information, though. They’re called models because they model a real-life entity that’s important to the application’s logic.
For example, if you were building an online reservation system for a hotel, your models might include the hotel, a room, an agent, a customer, the reservation, amenities, notes, invoices, receipts, and payments. What about a web-based application for teachers? You’d need to have data representing the school, its teachers, the students, courses, and grades, at a minimum. Each model in the application would represent a real-world object. Consequently, the larger and more complex the system, the more types of models you’ll have.
Let’s see what a model’s data would look like for our online employee directory. Remember that models mirror things in the real world. They contain not only data but behavior as well. In this case, you’re going to model a directory listing. I’ll keep it extremely simple to make sure the concept doesn’t get buried in too much code. Here’s the information we’re going to keep track of in each model:
The employee list inside the directory would be a collection (array) of these models. Figure 2.8 illustrates what this would look like from a conceptual standpoint.
To help you visualize what the models will look like when they’re added to the collection and rendered in the view, you can look at the screenshot of the employee directory application again (see figure 2.9). Remember, each model in the list is an object in an array. In this version of the screenshot, I’ve superimposed a snapshot of the data inside each model that has been added to our list. This will help you see the models in action.
Now that you have a mental picture of how the employee models will be used, let’s talk about how a model is defined in each type of framework. How you create a model in an MV* application varies, depending on the framework you’re using. As I mentioned, the framework might not be an exact match for one of the traditional design patterns, but the implementation will certainly be influenced by the pattern.
In some MV* implementations, the model is just the data itself, not an explicit structure prescribed by the framework. This data can be from any source, including POJOs and HTML form controls in the UI itself. There are no restrictions on what you can use for the source data when the model is implicit. This is the case for both Knockout and AngularJS.
For example, in our fictitious employee directory, we need the data for the employee model to come from the entry form the user fills out. So instead of creating a JavaScript object or getting JSON from the server, we need to capture data directly from the INPUT fields of our HTML form when creating each entry in the directory’s list.
AngularJS provides an easy shortcut for this. If you need your model data to come from an INPUT field, you can add a custom attribute called ng-model to each field (see listing 2.1). The attribute declares that model data is sourced from the HTML form element where the attribute is placed. The attribute magically establishes the form-Entry model if it doesn’t already exist and gives it a property called firstName. Then it ties formEntry.firstName to this INPUT field.
Once established, the model is readily available in your JavaScript code. One of the many benefits of using an MV* framework is keeping the complex, boilerplate code that marries the data and UI external to our application’s logic. This one attribute is a great example.
In Knockout, the model is again implied, not explicitly declared (see listing 2.2). In this framework, you add custom attributes to each INPUT field just as we did in the AngularJS version. This time, the attribute is called data-bind.
With Knockout (in true MVVM fashion), the attribute ties the INPUT field with a matching property in the ViewModel. In turn, our JavaScript code gains access to the field through the ViewModel.
With both AngularJS and Knockout, your model could have been any data source. Because you’re working from an entry form, that’s where you needed your model’s data to come from. In both cases, no model object was explicitly defined. Instead, each framework provided a custom attribute you could add to the HTML to establish the entry form as the source of the model’s data. Now let’s see how to create a model in Backbone.js, where models are explicitly defined in code.
In MV* implementations where an explicitly declared model is required by the framework, the model is created as a JavaScript object. Backbone.js is a prime example of this. Backbone.js models can have logic in addition to data, such as validation, default data, and custom functions. You also inherit a lot of functionality. Just by extending the framework’s model to create your own, you automatically receive a wide variety of base functionality without even writing code.
The ability to immediately inherit a lot of functionality makes creating a model in these types of frameworks powerful and flexible. For example, in Backbone.js you can create a bare-bones model in a single line of code:
var EmployeeRecord = Backbone.Model.extend({});
To use the object, you create a new instance of it and call any functions it has available out of the box. With this single declaration, your model right away has a variety of built-in behaviors such as validation, functions to execute RESTful services, and much more. See the online documentation for the full list (http://backbonejs.org).
It’s equally as easy to assign properties to a Backbone.js model. To create a new property called firstName and set its value to Emmit, you can either pass in {firstName : "Emmit"} to the object’s constructor or use the model’s built-in set method:
var employee = new EmployeeRecord({}); employee.set({firstName : "Emmit"});
The following listing illustrates the Backbone.js version of the employee model for our online directory. The validation needed for the directory example makes the source for the model quite verbose.
There seems to be a lot more going on than there is. We inherit the power of the Backbone.js model just by extending its base object. But the framework leaves it up to you as to how you want to validate the data. It gives you a couple of hooks with validate(attrs, options), and you can fill in the rest however you want.
The term binding is another concept you should understand if you plan on using an MV* framework. This term is used frequently when talking about UI development. In plain English, it means to tie or connect two things together. In UI development, whether we’re talking about desktop programming with a language like .NET or web development with MV*, we mean linking a UI element in the view (such as a user input control) to something in our code (for example, a model’s data).
It doesn’t have to be just data, though. Different libraries and frameworks offer different types of bindings. Styles, attributes, and events such as click are just a sampling of what can be bound to the UI. The binding types that are available vary, depending on the framework. You’ll look at the code for a few approaches in this section, just to illustrate.
How exactly do we declare a binding in our application? MV* frameworks make binding something in our code to an element in the UI simple. Understanding how to declare a binding starts with getting to know the syntax.
Binding syntax comes in two flavors:
With both types, the binding syntax is freely mixed with the HTML of the template. Table 2.1 lists a few examples of the binding syntax used by some popular libraries/frameworks. This is by no means an exhaustive list, but it should give you a general idea.
Framework/library |
Type |
Example |
---|---|---|
Knockout http://knockout.com | Attribute | data-bind="text: firstName" |
AngularJS (type 1) https://angularjs.org | Attribute | ng-bind="firstName" |
AngularJS (type 2) | Expression | {{ firstName }} |
Mustache http://mustache.github.io | Expression | {{ firstName }} |
Handlebars http://handlebarsjs.com | Expression | {{ firstName }} |
Underscore.js (default) http://underscorejs.org | Expression | <%= firstName %> |
Keep in mind, also, that the table is using simple text bindings to illustrate syntax styles. As noted previously, things other than data, such as events and CSS styles, may also be supported. See the documentation for each library/framework to see the complete list of bindings supported and additional usage instructions.
After you look at the documentation of the framework or library to get a feel for the syntax, the next thing to understand about binding is the directional flow of data in the binding.
Binding something in our code to a visual element in the view can be bidirectional, single-directional, or a one-time binding. The type of binding relationship is also established via the MV* framework.
In bidirectional, or two-way, binding, after the binding link is established, changes on either end cause updates on the opposite side. This keeps the two sides in sync. In a web application, two-way binding is associated with UI controls, like those in a form, that support user input.
Knockout is a great library to illustrate the concept of two-way binding. Binding is, after all, this library’s main purpose. As you saw previously, creating a binding is as easy as typing a custom attribute called data-bind right in the HTML. The data-bind attribute tells Knockout that something in the UI is going to be bound to a property in a ViewModel. In the following example, we’re binding the value of an INPUT control to a ViewModel property called firstName:
<input data-bind="value: firstName" />
For the other half of the two-way binding relationship, you tell Knockout you want the property to be observed for any changes by wrapping its data in a Knockout observable object. (Remember observables from the Observer pattern?)
var myViewModelObject = { firstName : ko.observable("Emmit") };
Because of the two-way binding established by this small amount of code, these two items will stay in sync automatically.
Binding is just as easy, or more so, with AngularJS. You already saw AngularJS’s two-way binding in action during our discussion of models. You add the attribute ng-model to the HTML of the INPUT tag:
<input ng-model="firstName" />
On the JavaScript code side, you have a $scope object instead of a ViewModel. Scopes are similar in that they sit between the view and our JavaScript code and give us access to the model.
One nice thing about this framework is that AngularJS’s magic automates a lot of the two-way setup. First, the $scope object automatically monitors models for changes. Second, you don’t even have to create the $scope object; AngularJS will hand it to you, if you ask for it. Then, in your code, you can refer to the property via the $scope object like this:
$scope.firstName
That’s it. Now both the INPUT field and the property are bidirectionally bound. Changes on either side affect the other. Pretty easy, huh?
You’ve seen the approaches to two-way binding from two different frameworks. Even so, the concept remains the same in both. Now let’s take a quick look at binding in a single direction.
When binding is single-directional, or one-way, changes in the state of the source affect the target but not the other way around. This type of binding is normally associated with HTML elements that don’t require any input from the user, such as a DIV or SPAN tag. With these types of elements, you’re interested in its text, not its value. You still access the data on the JavaScript side in the same manner, but in the template you choose the attribute specifically for one-way text binding.
With Knockout, you change the word value to text:
<span data-bind="text: firstName"></span>
In AngularJS, the attribute itself changes from ng-model to ng-bind:
<span ng-bind="firstName"></span>
Once again, you can see that the binding concept remains the same even though you’re looking at different MV* frameworks.
Knockout provides an additional way to make even bindings for user input one-way, in case you need that behavior. You just remove the observable “wrapper” from the ViewModel property like this: firstName : "Emmit".
You might be wondering at this point, why bother with the one-way types? Why not use two-way binding always? Well, usually something as magical as automatic, two-way binding comes at a cost. Two-way binding has slightly more overhead. No need to panic and avoid it, though. For most views, this overhead is negligible. But if you have a ton of bindings throughout your application, you should use any means to save on overhead.
If your view receives input from the user, and you need the data and view to stay constantly in sync, use two-way binding. When you have read-only UI elements, use one-way binding. One-way will keep the view updated when the model changes but doesn’t bother with trying to monitor the view side, because the element is read-only.
One-time binding is a type of one-way binding that happens only once. Nothing is automatically observed for changes. No subsequent updates occur if the source changes or the target changes.
With one-time binding, after the template and the data are combined and rendered as the view, the process is done. If new changes need to be applied to the view, the entire process starts over. The previous view is destroyed, and the new data is combined with the same template to generate the view anew.
I’ve saved Backbone.js for this section. The typical approach for rendering templates when using Backbone.js is through one-time binding (though some Backbone-compatible libraries and plugins offer the other two types). With AngularJS and Knockout, after the bindings are established, they’re reused. In Backbone.js, the general idea is that when new data is needed, the view is destroyed (with the bindings) and re-created. I’ll talk more specifically about templates in the next section.
Backbone.js doesn’t have templating/binding capabilities built in but instead lets you pick the outside library of your choice for the task. Its default is the utility library Underscore.js.
To recap the types of bindings we discussed, consider table 2.2.
Binding type |
Behavior |
---|---|
Two-way | Bidirectional—keeps data and view constantly in sync. |
One-way | Single-directional, or one-way—changes in the state of the source affect the target, but not the other way around. |
One-time | One-way—occurs only once at render time, from model to view. |
In the next section, you’ll see binding in action with template examples from our online employee directory.
A template is a section of HTML that acts as a pattern for how our view is rendered. This pattern can additionally contain various types of bindings and other instructions that dictate how the template and its model data get processed. A template is also reusable, like a stencil.
One or more templates are used to create a view, with complex views often having multiple rendered templates on the screen at the same time. The part of the MV* framework, whether built in or via an outside library, that marries the template and the model’s data is generally referred to as a template engine. Figure 2.10 illustrates the marriage of data from the employee directory form (our model) and a template to arrive at what the user sees onscreen.
You should now be able to recognize bindings when you see them, so let’s take a look at some real examples of templates and their bindings taken from our MV* project. I’ve highlighted the bindings so you can easily see which part of the template is the binding and which part is just the HTML.
One thing all templates have in common is that they represent some part of our view. What this means for you as a developer is that, apart from the binding syntax, views are just HTML. This also means that if you have a web designer on the team, views can be constructed by the designer as well.
The following listing shows our Knockout template. Notice that it has custom attributes, but apart from that it’s normal HTML.
Do you remember from our discussion of binding syntax that with AngularJS we could use either expressions or attributes? For the online directory project, we’re using expressions just to demonstrate those (see the following listing). You can use ng-bind if you prefer the attribute style.
What’s neat about templates in Backbone.js is that you aren’t confined to a particular template syntax, because there’s no built-in template library. Backbone.js allows you to use the template engine of your choice. In our directory project, we’re using the default, which is Underscore.js (see the following listing). Underscore.js has a default delimiter of <%= %> for its expressions, but the delimiters can be anything you want (including {{ }}).
These examples provide a good introduction to templates and the various binding styles that are used. One thing we haven’t talked about yet, though, is how the process gets triggered.
In AngularJS, the rendering of a template happens automatically, as soon as the application starts. AngularJS searches through the DOM for its custom attributes, including those for binding templates.
In other frameworks, this step isn’t difficult but may be a little more explicit. Knockout, for example, requires a one-liner in your JavaScript code for each ViewModel to activate the bindings:
knockout.applyBindings(myViewModel, $("#someElement")[0])
Knockout has a special function you call, applyBindings, which renders the template with the model data supplied by the ViewModel. The first parameter is the ViewModel itself. The second is the place in the DOM you want Knockout to start looking for bindings for the given ViewModel. The second parameter is optional, but for efficiency you should use it in order to confine the binding process to a particular parent element.
$("#someElement")[0] is the way in jQuery to access the underlying DOM object referenced in its selector, because jQuery doesn’t know how many elements will be a match for a given selector. You can also use the Java-Script document.getElementById("someElement") method as the second parameter.
In Backbone.js, rendering the template is a bit more of a manual process. The framework provides template and render as hooks for the external template engine of your choice. To render the template to the screen, you have to run it through the compile-and-render process. You’ll see the view in its entirety in the next section, but for now let’s focus on the rendering of the template, as shown in the following listing.
The preceding code seems quite verbose when compared to the other two frameworks. The ability to choose whichever template engine you want is a great trade-off, though.
You now know what templates are and how to create them. The lingering question you might have is where do you keep their source code?
Templates can either be included in the initial download of your SPA (inline) or downloaded on demand as external partials (or fragments).
If your template uses the expression style of binding syntax and isn’t downloaded on demand, you’ll need to place it inside SCRIPT tags. You use SCRIPT tags to avoid accidentally showing the user the binding code before the render process happens. The browser won’t try to display code within the SCRIPT tags.
To prevent the browser from trying to execute the script as JavaScript code, you’ll need to give the SCRIPT tag’s Multipurpose Internet Mail Extensions (MIME) type something other than text/javascript or application/javascript, as shown in the following listing.
If your inline template uses attributes for its binding syntax, there’s nothing else special you need to do. SCRIPT tags aren’t needed. Also, because attributes aren’t displayed anyway, there’s no chance the user will accidently see the bindings before the view is rendered.
If you download the template on demand, there’s no need for SCRIPT tags, even if you’re using expressions. The dynamically fetched template can be used directly by the template engine, which avoids the issue altogether.
As noted previously, these on-demand templates are sometimes referred to as partials or fragments. They’re not part of the initial HTML document that’s loaded with your application. Instead, they’re fetched as snippets of source code directly from the server at runtime.
Now that you have a good idea about models, binding, and templates, you need to finally see how they culminate into the view the user sees and interacts with. In the next section, you’ll look at how MV* frameworks approach views.
As you saw during our discussion of templates, frameworks such as Knockout and AngularJS use declarative bindings in their templates, usually in the form of special attributes added to HTML elements. In these frameworks, the templates and views are pretty much the same thing. Thus, when composing views in these frameworks, you need to decide only whether the templates will be kept inline or downloaded on demand. This is more of a design issue.
In a code-driven framework like Backbone.js, the approach is to programmatically create the view. The following listing is an example from our directory application of a Backbone.js view. Don’t worry if you don’t understand everything in the example right now. When you’re ready, appendix A contains a complete walk-through of the code.
Backbone.js allows you to define traits for your view, such as its CSS class name and the type of element it will be. Additionally, you’re given the freedom to define key milestones in the life of the view, such as its rendering and removal, in any manner you wish.
The discussion on views rounds out our conversation of basic MV* concepts. It’s great to understand the concepts, but what does an MV* framework do for us? In the next section, I’ll discuss how using an MV* framework can make our lives as SPA developers much easier and our code much cleaner.
Deciding to use any external software in your project shouldn’t be taken lightly. You are, after all, introducing a dependency. That being said, however, when its benefits exceed the costs, a new dependency is worth considering. This section presents some of the key benefits of using MV* frameworks.
As mentioned previously, MV* frameworks provide a means to segregate JavaScript objects into their basic roles based on their underlying design pattern or patterns. Each part of the code can be focused on a particular responsibility to the application.
This overarching concept of separation of concerns helps us design objects with a particular purpose. Models can be dedicated to data, views can be dedicated to the presentation of data, and components such as controllers, presenters, and ViewModels can keep these two parts communicating with one another without being joined at the hip. The more dedicated an object is to a singular purpose, the easier it is to code, test, and update after it’s in production.
MV* frameworks also reduce the tendency to write spaghetti code by providing framework elements that require us to write code in a particular way to facilitate loose coupling. This keeps our HTML as clean as possible by removing embedded JavaScript and CSS code. It also keeps our JavaScript free from deep coupling with DOM elements.
Here’s a classic AngularJS example demonstrating how MV* can make our code cleaner. Figure 2.11 shows a SPAN tag (right) mirroring what’s being typed into an INPUT field (left).
Let’s create the example from figure 2.11, first written as spaghetti code and then with AngularJS. Listing 2.10 is the source code, written as tightly coupled HTML and JavaScript. Tightly coupling means making direct references or calls from one function or component to another. This joins them at the hip, so to speak.
Writing code this way works, but it can prove to be difficult to read and a pain to update later. If an entire single-page application were written like this, you can imagine how monumentally difficult it would be to maintain.
The code in listing 2.10 dynamically updates the SPAN as you type but is hard to read and difficult to maintain.
Now, in the following listing, we use the AngularJS framework’s ability to abstract away much of the boilerplate code needed to achieve the same results.
Believe it or not, that small bit of code is all that’s needed. Of course, this is a contrived example. Even using a framework as powerful as AngularJS, complicated applications will have complicated logic. For all the reasons I just mentioned, though, you’ll spend more of your time writing business logic, not the low-level, routine plumbing.
MV* frameworks also simplify some of the tasks we as developers deal with on a regular basis. Take, for instance, repetitively printing out the data from a list, complete with HTML markup, to the screen. That’s a run-of-the-mill task, but the mechanics involved take a good deal of code to pull it off. Moreover, we find ourselves repeating the same code over and over every time we need to do this.
Let’s consider the employee directory from the beginning of the chapter. One of the requirements is to be able to add an employee’s data as an entry to a list. If you had to code all the mechanics by hand, you’d find yourself manually creating DOM elements for each entry in a JavaScript loop. The code wouldn’t be pretty, and chances are it wouldn’t be reusable for other tasks.
MV* frameworks take the drudge work out of tasks like this. Take the code in listings 2.12 and 2.13. This is how we’re adding entries to our list using Knockout. Listing 2.12 is from the HTML side of things. For brevity, this isn’t the entire source code, only the portion to add to the directory’s employee listing. The complete source code can be found in appendix A.
Notice that we don’t have any JavaScript code in our HTML. The following listing shows the ViewModel backing this section of the template.
With a few subtle attributes and a minimal amount of code, we were able to accomplish our list of management needs. Taking the grunt work out of routine tasks like this greatly simplifies life as a programmer.
From a development standpoint, being able to devote your time and energy to your business logic is a definite boost to productivity. When we do decide to use an external library or framework, we’re removing the burdens of having to maintain that part of the code base ourselves. We also use the expertise of their authors in the areas that the particular framework covers. Sure, you could create your own routines to do the same thing, but it would take an enormous effort to get it to the level of the MV* implementations out there.
You also have an incredible amount of community-based knowledge on the web for most libraries/frameworks, should you run into problems. Most authors of MV* have mechanisms for reporting bugs too. This means that periodically code fixes are tested and released, without you having to spend your time on the issue.
If you’re doing everything yourself, you’re maintaining your own business logic, plus the extra bug fixes and testing for all of the structural code provided from external libraries/frameworks.
As you’ll hear me repeat throughout this book, writing a robust web application with a clean, scalable code base is already difficult. This difficulty can be compounded in a single-page app. So the last thing you need is to have everyone on the development team writing code in completely different styles.
You want to be able to read your teammate’s code as if it were your own. Otherwise, you’ll continuously waste time deciphering some “foreign” style of coding before you can get around to updating it. Even if you’re alone, not working with a team, having uniform code standards will help you when it’s time to revisit something you wrote to make changes.
MV* libraries and frameworks have certain conventions that must be followed in order to use the software. This will compel you to write your application’s code in a more formal, standardized way.
As discussed previously, MV* frameworks inherently promote the separation-of-concerns concept. This, in turn, also makes a project more scalable, because loosely coupled objects can be reworked with minimal effect on other objects.
Objects can also be swapped out entirely to make room for new functionality without causing a huge ripple effect throughout the project. This allows the project to grow more gracefully, because code changes tend to have much less negative impact.
Now that you’ve seen some of the core MV* features, which style seems easier? Which is more difficult to use? You’ll have to be the judge. Some people don’t like having the declarative style of bindings freely mixed with the HTML page itself as you see with MVVM. Others prefer it over having to use so much boilerplate code to create the view.
Although you’ll ultimately have to decide which framework is right for you and your project, section 2.4 will give you a few things to think about as you’re making your decision.
After you’ve decided you do want to use an MV* library or framework, you have a lot to choose from. Even if you decide on a particular style you like better, you’ll still have a lot of candidates to weed through. Just to give you an idea, here are some popular client-side MV* options available at the time of this writing:
As you can see, you have quite a few choices. And those are the libraries/frameworks themselves. If you decide to choose a framework that’s not all-inclusive, the lists of libraries and frameworks to handle the other features, such as routing and view management, are nearly as long. In a few years, we went from relatively few choices to an overwhelming number of them.
It’s rather unproductive to try to point out which ones are “better” than others. It’s all a matter of opinion. Also, because they’re all different and have a different number of features and styles, it’s hard to make an apples-to-apples comparison. But I can offer a list of things to keep in mind as you’re making your decision:
As you can see, you have many factors to think about when choosing an MV* framework. But you’re now acquainted with the traditional design patterns and the basic core concepts. You’ve also been exposed to some of the design differences of MV* libraries/frameworks. With that and this list of points to consider, you’ll be able to make a more informed choice when the time comes.
Now here’s a challenge for you, to see what you’ve learned in this chapter. Let’s see if you, on your own, can use one-way bindings to create a simple view. Let’s pretend that your local library wants to begin offering e-books online and has reached out to the community for help. The library already has converted its first set of books to e-books but needs someone with web development skills to set up the e-book site. Pick any one of the three MV* frameworks from this chapter (or a different one if you prefer) and create a view that’s contains a list of a few books. The view should have the following format:
Understanding the information in this chapter will help you going forward. Let’s review: