Appendix A. Employee directory example walk-through

This appendix covers

  • Walk-through of the Backbone.js example
  • Walk-through of the Knockout example
  • Walk-through of the AngularJS example

You saw various code examples illustrating the components of our employee directory throughout chapter 2. In doing so, you gained a better understanding of the role MV* libraries/frameworks play in the creation of an SPA.

In this appendix, you’ll walk through the complete source code for our directory by using each of our selected MV* frameworks. This will help you understand the complete picture if you decide to try one of these yourself. Before you start, let’s go over your objectives again:

  • Create a simple SPA to enter employee information.
  • Build an easy-to-use UI for entering each employee’s first name, last name, title, email, and phone.
  • Keep track of each entry as part of a list, with the screen split between the entry form on the left and the directory’s entry list on the right.
  • Have two buttons on the entry side of the SPA: one to add a new entry and one to clear the form.
  • Have one button next to each entry to remove the entry from the list.
  • Have indicators next to each entry field to denote whether the field’s entry requirement has been met (each indicator should update as the user types).

In other parts of the book, you’ll learn more-advanced topics, such as routing and server transactions. For your first foray into the world of the SPA and MV*, you’ll avoid the use of the router to keeps things simple. You’ll also keep your employee data in memory.

Now that you’ve reviewed the objectives, let’s take another look at the screenshot you first saw in chapter 2. This screenshot, shown in figure A.1, is the final product. The application will look and behave the same for each MV* framework used.

Figure A.1. Screen capture of the online directory. The user enters information in the form on the left. Valid entries appear in a list on the right.

This design, though simple, takes you through the paces of designing views, templates, bindings, and models, as well as other framework-specific components. It’s also nice in the sense that, although you’re not communicating yet with the server, you’re still carrying out CRUD operations on a list of items.

A.1. CSS

You’ll use the same CSS files for all three versions. The following listing is your default style sheet. You’re merely setting the stage with the font and the styles for your main section and header.

Listing A.1. default.css
html, body {
    font-family: arial;
}

main {
    display: block; /* for IE */
    width: 800px;
    border: 1px solid #909092;
    background-color: #B7B8BA;
    border-radius: 10px;
    margin: 15px;
    padding: 5px 15px 15px 15px;
    box-sizing: border-box;
}

main > header {
    font-size: 25px;
    font-weight: bold;
    margin-bottom: 15px;
}

The following listing defines the styles for the entry form and the set of entries that will be displayed when the user adds directory entries.

Listing A.2. entries.css
.entries {
    background-color: #D9D9D9;
    border: 1px solid #6D6D6D;
    border-radius: 10px;
    overflow: hidden;
    box-shadow: inset 0 0 3px 0 #6B6B6B;
    box-sizing: border-box;
}

.entries * {
    box-sizing: inherit;
}

.entries form {
    float: left;
    width: 45%;

    padding: 5px 0 0 5px;
    font-size: 18px;
}

.entries p {
    margin: 12px 0;
}

.entries label {
    font-style: italic;
    line-height: 1.4em;
}

.entries input {
    padding: 2px;
    box-shadow: 0 0 8px rgba(0, 0, 0, 0.3);
    border: 1px solid #999;
    line-height: inherit;
    font-size: inherit;

}

.entries .error-message {
    float: right;
    padding: 1px;
    margin-right: 15px;
    color: #DC3E5E;
    font-size: 12px;
    font-weight: bold;

}

.entries button {
    cursor: pointer;
    background-color: #EFEFEF;
}

.entries button::-moz-focus-inner {
    padding: 0;
}

.entries form button {
    box-shadow: 0px 2px 0px rgba(0, 0, 0, 0.5);
    font-size: 16px;
    border-radius: 5px;
    padding: 7px 20px;
    font-weight: bold;
    border: none;
}

.entries .entry-list {
    margin: 0 0 0 45%;
    width: 55%;
    height: 458px;
    border-left: 1px solid #9F9F9F;

    background-color: #EFEFEF;
    box-shadow: inset 0 0 3px 0 #6B6B6B;
    padding: 5px 0 0 0;
    overflow-y: auto;
}

.entries .entry-list li {
    list-style-type: none;
    margin: 5px 15px 15px 15px;
    border-bottom: 1px solid #9F9F9F;
    font-weight: bold;
}

.entries .entry-list .remove-entry {
    float: right;
    border: 1px outset #D9D9D9;
    padding: 5px;
}

Because it’s such a small application, I’ve given it a fixed size on the screen. This also gives you just one general area of the screen to focus on while you get your bearings with MV*.

A.2. Backbone.js example

Let’s begin with Backbone.js. Remember that it’s our MVC/MVP-like framework. It’s a little more feature rich than Knockout, but it doesn’t have nearly as many bells and whistles baked in as AngularJS. It has an open design, though, allowing you to extend it through code or through third-party plugins to add features.

A.2.1. Downloading your dependencies

To start, you’ll download what you need. You’ll use several third-party libraries/frameworks to extend Backbone.js in this example. I won’t cover all of the third-party libraries/frameworks in depth, but I’ll discuss how they’re being used here (see table A.1).

Table A.1. Dependencies for the Backbone.js version of the example

Framework/library

URL

Comments

Backbone.js http://backbonejs.org You can select either the minified (compact) version or the nonminified (readable/formatted) version. Both work the same. The readable version is helpful for understanding the source code. For deployment, always use the minified version.
jQuery http://jquery.com A great overall utility library that excels at DOM manipulation and event handling.
Underscore.js http://underscorejs.org Another utility library. This one is loaded with all sorts of programming helpers. You’ll specifically use it as your template engine.
Handlebars.js http://handlebarsjs.com Handlebars is another template library. At the time of this writing, Handlebars is a requirement for using Backbone.js, even if you don’t use it yourself.
RequireJS http://requirejs.org I discuss RequireJS in chapter 3. For now, just know that it’s used for asynchronous JavaScript module loading and dependency management. Using it, you’ll create code using the AMD specification (also discussed in chapter 3). You’ll also use two RequireJS plugins: domReady.js, which makes sure the DOM is ready before your JavaScript code acts on it, and text.js, which asynchronously downloads templates. Both plugins can be found at http://requirejs.org/docs/download.html.

A.2.2. Directory structure

I’ve tried to keep the directory structures fairly similar between the three versions. I do, however, use framework-specific folders where it makes sense. Figure A.2 is the directory structure for our Backbone.js version.

Figure A.2. Directory structure for the Backbone.js version of the application

This design is still by feature, as I talked about in chapter 1, with the feature being the directory (under components). Your feature folders under components can grow with the application. But within the feature, the directory structure represents a somewhat typical Backbone.js layout.

A.2.3. The shell

In typical SPA fashion, your index.html welcome file, shown in the following listing, is almost bare. Its main features are the style-sheet reference, the shell DIV for the application, and the RequireJS entry to kick-start the application.

Listing A.3. index.html

RequireJS dynamically and asynchronously loads any dependent files the application needs, including modules and templates. It also makes sure they’re ready for use before any code tries to use them. This way, you don’t have to worry about script order and the synchronous and blocking nature of SCRIPT tags. Again, don’t worry about RequireJS at this point. It’s just wrapping your JavaScript code into neat, self-contained modules.

A.2.4. main.js

This file contains the configuration preferences for RequireJS, shown in the next listing. Here you define a base URL and the paths relative to that where RequireJS can download each file and assign an alias to that path.

Listing A.4. main.js—RequireJS configuration

The last line is the starting point for the SPA. RequireJS makes sure that app.js is loaded before calling your application’s init() function.

A.2.5. app.js

Your app.js file, shown in the following listing, loads the user’s first viewable area with the SPA’s first HTML content file. The file doesn’t have to be called app.js, but that’s a typical naming convention for the first real code file. Any kind of pre-application work or setup goes in this file.

Listing A.5. app.js

RequireJS downloads all dependencies in your list to begin with. After the dependencies are in place, the application’s init() function is called. Here, you use jQuery to insert your first HTML content. When the DOM is ready, you create a new instance of your main Backbone.js view.

A.2.6. directoryContent.html

As you can see in the following listing, your HTML file looks clean. Backbone.js helps you interact with it without embedding JavaScript code.

Listing A.6. directoryContent.html

So far, so good. The HTML is nice and clean, void of JavaScript. It’s also readable. In the next section, you’ll see what the Backbone.js view looks like for this content.

Backbone.js objects

As you look at the JavaScript code for your Backbone.js version, one thing to keep in mind is that objects created using this framework extend an out-of-the-box object. You’ll define the prototype first and then use the new keyword to create a new instance of it anytime you want to use an object of that type. Your views extend Backbone.View, your lists extend Backbone.Collection, and your models extend Backbone.Model. By doing this, you inherit many out-of-the-box features. To see the full set of features and examples, visit http://backbonejs.org.

A.2.7. directory.js view

As you may remember from our discussion of Backbone.js views, a view is created in code. A template and a model are used to create the visual content (see the following listing). Also note that function calls beginning with an underscore (_) are functions from Underscore.js. This framework makes certain tasks, especially those related to arrays, easier.

Listing A.7. directory.js view

Backbone.js has a lot less magic going on than the other two MV* frameworks you’re using. As you can see in the listing, all of the work focuses on maintaining the state of the form as the user types in information.

A.2.8. entrylist.js view

Interaction with your list of entries is done through your entry list’s view (see the following listing). Here you declare a Backbone.js collection to manage your collection of entry models. You can think of a collection as a proxy to manage an internal array of models.

Listing A.8. entrylist.js view

In your view for the list of entries, you can listen to the Backbone.js collection and react whenever a new model is added. In this case, you’re rendering each model that’s added.

A.2.9. entries.js collection

This file is fairly simple, as you can see in the next listing. For your simple application, you need to define only a collection that will hold an EmployeeRecord model.

Listing A.9. entries.js collection

Make sure to include the model as a dependency. In the previous section, you saw that every time the form data is valid, one of these models is added to your collection.

A.2.10. employee.js view

You use the RequireJS text plugin to dynamically fetch and cache the employee template that contains placeholders for your model’s data (see the following listing). Because you’re performing template duty in this view, you’re also relying on Underscore.js as the template engine.

Listing A.10. employee.js view

In this view, you don’t do any data validation. That’s the model’s job. The view is all about what the user sees and interacts with. In this case, it’s just the employee entry itself. The view for the list will take care of the overall list UI. Here, you’re just concerned with taking your model data and marrying it to your template.

You also have an event on hand to call the removeEntry function if the X button is clicked by the user. Backbone.js has a little magic here for you. The destroy event is observed by your collection, which will remove the reference to the model. To round out the cleanup job, you also call remove() to remove any associated DOM element in the view and any event bindings associated with this view as well.

A.2.11. employeeRecord.js model

The following listing shows the employee model behind the view. Don’t worry that it looks like a lot of code. It’s a little verbose, but it’s just the validation needed by the model.

Listing A.11. employeeRecord.js model

Obviously, the main event in this model is the validation you’re doing. In Backbone.js, you can validate any way you want. The only thing you have to do if you want to make your errors available outside the model is to return them from the model’s validate method.

A.2.12. entrytemplate.html

You finally see the template for the employee’s data in the following listing. This file is nothing more than a snippet of HTML with template markup embedded. These snippets of HTML are also called partials or fragments.

Listing A.12. entrytemplate.html

As you may remember from chapter 2, the <%=%> mark the spots where your model’s data will be inserted. When the template engine “compiles” the template, it’s just turning the text string into a reusable function for generating the resulting view. When the view is rendered, the model’s property values are inserted for its final presentation state. Keep in mind that Backbone.js is open minded when it comes to template engines. I’m using Underscore.js here, but many more options are available. Another popular choice is Handlebars.

A.3. Knockout example

Your second POC (proof of concept) was created using Knockout, which is squarely in the MVVM camp. Although it excels at data binding, you’ll have to seek out other frameworks/libraries (such as Durandal) to fill any other SPA needs you have. Knockout sticks mainly to what it’s great at: binding. For those who’d rather not have a magic black-box solution to MV* and would rather take an a la carte approach, Knockout is a perfect choice. For this POC, you’ll incorporate a few of the same extra-helper libraries that you used in our Backbone.js version.

A.3.1. Downloading your dependencies

Here’s the list of what you need for this POC. If you’ve already downloaded some of these for the Backbone.js POC, there’s no need to download them again. You can use them again here (see table A.2).

Table A.2. Dependencies for the Knockout version of the example

Framework/library

URL

Comments

Knockout http://knockoutjs.com As with Backbone.js, you can select either the minified (compact) version or the nonminified (readable) version. Remember, the readable version is only for understanding the source code and debugging. For deployment, always use the minified version.
Knockout Validation plugin https://github.com/Knockout-Contrib/Knockout-Validation Because Knockout doesn’t come with any kind of validation functionality built in, you can either roll your own or use a plugin. Feel free to create your own if you prefer. For this example, you’re using a popular plugin that’s easy to use and devoted to validating your ViewModel’s data.
jQuery http://jquery.com A great overall utility library that excels at DOM manipulation and event handling.
RequireJS http://requirejs.org I discuss RequireJS in chapter 3. For now, just know that it’s used for asynchronous JavaScript module loading and dependency management. Using it, you’ll create code using the AMD specification (also discussed in chapter 3). You’re also using two RequireJS plugins: domReady.js, which makes sure the DOM is ready before your JavaScript code acts on it, and text.js, which asynchronously downloads templates. Both plugins can be found at http://requirejs.org/docs/download.html.

A.3.2. Directory structure

As before, you’ll keep the directory structures fairly similar among the three versions but use framework-specific folders where it makes sense (see figure A.3).

Figure A.3. Directory structure for the Knockout version of the application

This directory structure is, again, by feature, as covered in chapter 1. Your feature is still a directory, and the rest of the structure represents a somewhat typical Knockout layout.

A.3.3. The shell

In typical SPA fashion, your index.html welcome file, shown in the following listing, is almost bare. Its main features are the style-sheet reference, the shell DIV for the application, and the RequireJS entry to kick-start the application.

Listing A.13. index.html

As with Backbone.js, you’re using RequireJS to manage your dependencies for you.

A.3.4. main.js

This file provides the configuration preferences for RequireJS, as shown in the following listing. Here you define a base URL and the paths relative to that where RequireJS can download each file and assign an alias to that path.

Listing A.14. main.js—RequireJS configuration

The shim section is a special RequireJSism for helping non-AMD modules play well with the AMD ones. It can also be used to define other load properties, such as dependencies. The last line is the starting point for the SPA. RequireJS makes sure that app.js is loaded before calling your application’s init() function.

A.3.5. app.js

Your app.js file, shown next, loads the user’s first viewable area with the SPA’s first HTML content file. The file doesn’t have to be called app.js, but that’s a typical naming convention for the first real code file. Any kind of pre-application work or setup goes in this file.

Listing A.15. app.js

RequireJS downloads all dependencies in your list to begin with. After the dependencies are in place, the application’s init() function is called. Here, you use jQuery to insert your first HTML content. When the DOM is ready, you call the init() function inside your ViewModel’s JavaScript model.

Because you’re creating your RequireJS modules as singletons (only one instance), creating a function that doesn’t get called repeatedly is a nice place to put your call to apply the ViewModel’s bindings. It doesn’t have to be called init(); that’s just what I called it.

Remember, you want to apply bindings only once for any given DOM element. Unlike Backbone.js, where templates are re-created for each new data set, ViewModels normally live perpetually. Making the call to apply bindings multiple times leads to memory leaks, because you’re replicating event handlers with each call.

Tip

Call applyBindings() or applyBindingsWithValidation() only once per subtree inserted into the DOM to avoid duplicating event handlers.

A.3.6. directoryContent.html

Your HTML file, shown in the following listing, doesn’t mix in any JavaScript, but as you can see, it’s a little more verbose than the Backbone.js file. As in most of life, there’s a trade-off: with Knockout, your JavaScript might be a little lighter, but adding the custom Knockout attributes (called declarative bindings) makes the HTML a little heftier.

Some people prefer cleaner code if the only trade-off is a little more verbose HTML. Others don’t like the idea of attributes from the framework being added to the HTML. It’s all subjective. But then again, that’s why it’s a good idea to do little POCs like this so you get hands-on experience with a few of your top choices and can decide what’s right for you.

Listing A.16. directoryContent.html

The HTML looks a little more verbose than it is, because I’m trying to fit it within the width of this book’s page. When you try the source code, feel free to adjust the formatting so it’s the way you like it.

A.3.7. directory.js

In this POC, the module containing your ViewModel has the most code (see the following listing). Not all ViewModels will be exactly like this. Some, dealing only with the data, might be small. Others, with lots of logic, might be large.

Listing A.17. directory.js

Remember that in MVVM, the observable wrapper around the POJO (plain old Java-Script object) property establishes the magic link between it and the form. The entire first half of the ViewModel is the binding of your form’s entry fields so you can interact with them.

The observables look a little different only because there’s a little extra information in there to tell the validation plugin how the property should be validated. The required option is a validation type that comes with the plugin. The validation information for the phone and email are for a custom validator I built for this specific type of UI design. The plugin has some options for phone and email, but they weren’t exactly what was needed. Fortunately, the plugin is extremely flexible, and you can create your own.

One thing I haven’t talked about is a type of Knockout observable called a computed observable. This isn’t specifically MVVM but rather Knockout-specific (though other MVVM frameworks may include this). This is just a fancy term to mean the value is derived programmatically, rather than as a straight assignment. This code determines whether the observable gets a value of true or false.

Tip

Use a computed observable if the value of the observable needs to be programmatically derived, rather than a simple assignment.

After the assignment portion of your ViewModel and the validation, you have some bindings to handle the adding and removing of list entries. You also have one to remove an entry for the list. The interesting part here is that the declarative part of the binding (attribute in the HTML) is doing the heavy lifting of looping through the list of values in your array and making sure the UI reflects its current state. You don’t have to code that part yourself.

Thanks to the two-way syncing going on, Knockout keeps track of everything that’s happening with your observable array and updates the DOM and the ViewModel accordingly. All you have to do is add and delete entries, and Knockout takes care of the rest. Neat, huh?

The section of code added to create the custom validation rule is specific to the validation plugin. You’re following the rules from the website on how to define your own validator. You’re telling the plugin that if the field is empty, it’s invalid and to use the blank message (the word Required). If the field isn’t blank but is still invalid, use the invalid message (the word Invalid).

The validator is robust. You can use a ton of other features in your own SPA. Follow the same link as the dependency download to learn more.

The last part of your module is its init() function. Remember, this code runs only once. That makes it ideal for any setup work. Here you’re using jQuery to add the template to the DOM and apply your bindings. Normally, the Knockout function to call is applyBindings(), but with the validation plugin, you need to call applyBindingsWithValidation(). You’re passing in three parameters: the ViewModel itself, the DOM node where the bindings should be applied, and the configuration for the validation plugin.

A.3.8. entrytemplate.html

Your last listing, shown next, is the template for the employee’s data. This file is nothing more than a snippet of HTML with template markup embedded. These snippets of HTML are also called partials or fragments.

Listing A.18. entrytemplate.html

As with your Backbone.js template, this file is fairly simple. One stark difference with MVVM is the use of declarative bindings (special attributes). Remember that Backbone.js uses placeholders to mark where the template engine will insert data and create a new view. With MVVM, the template and the view are one and the same. The custom Knockout attributes link this section of the DOM with properties from the ViewModel. Knockout handles the data sync going on under the covers.

A.4. AngularJS example

One thing you’ll notice immediately about AngularJS is the reduction in the number of files you need. One reason is all the out-of-the-box magic that comes with the framework.

A.4.1. Downloading your dependencies

One thing you won’t be using with AngularJS is RequireJS. In the pre-version-2 AngularJS, the framework has its own proprietary module system. So to keep things in line with “the Angular way,” you’ll use AngularJS modules (see table A.3). This also further reduces the number of dependencies.

Table A.3. Dependencies for the AngularJS version of the example

Framework/library

URL

Comments

AngularJS https://angularjs.org Because it’s an all-in-one system, this download covers most MV* requirements you have.
jQuery http://jquery.com A great overall utility library that excels at DOM manipulation and event handling.
Note

AngularJS comes with a subset of jQuery called jqLite. If jQuery isn’t present, AngularJS falls back on its own version; otherwise, the full jQuery is used.

A.4.2. Directory structure

Figure A.4 illustrates your nominal directory with AngularJS. Even using AngularJS, you can still have a “by feature” directory structure.

Figure A.4. Directory structure for the AngularJS version of the application

A.4.3. The shell

Again, your index.html file (see listing A.19) is minimal. Notice, however, that in the AngularJS version you need to define where the overall SPA begins by using the ng-app directive. Remember that directives are special HTML attributes that tell AngularJS what you want to do. From the AngularJS documentation:

Use this directive to auto-bootstrap an AngularJS application. The ngApp directive designates the root element of the application and is typically placed near the root element of the page—for example, on the <body> or <html> tags.

Listing A.19. index.html

You’re also using an AngularJS directive to tell AngularJS to dynamically fetch your SPA content for your shell.

One thing to take note of here with your index.html page is that AngularJS will manage only its own JavaScript modules. If you have other third-party libraries to include, you’ll need to use a standard SCRIPT tag.

A.4.4. app.js

Your app.js file, shown next, is miniscule. There seem to be a million different ways to structure your AngularJS code. Feel free to do things your own way. Despite being a magic framework, AngularJS is flexible on this point.

Listing A.20. app.js

As mentioned, there are many ways to configure your AngularJS project. For larger projects, keep modules in separate source files. This makes development and maintenance much easier. In chapter 9, I discuss build processes to minify and combine your files into as few files as possible. For this simple application, you didn’t need to do anything elaborate.

A.4.5. directoryContent.html

Like your Knockout content, the AngularJS version is verbose, as you can see in the next listing. You’re using a lot of built-in AngularJS directives to take care of some of the plumbing for your UI’s behavior.

Listing A.21. directoryContent.html

Again, the HTML looks a little more verbose than it is, because I’m trying to fit it within the width of this book. It uses directives to do everything you did in the Backbone.js and Knockout versions. The ng-model is for the value binding, and ng-click is for the button clicks. For validation, you needed to add only the required keyword to tell AngularJS that the model is invalid if this field is empty.

Errors are tied to a particular model binding, so you can show/hide what’s in your span tag by using a combination of the ng-show directive and $error. The keyword after $error is the type of validation that should trigger ng-show. In this case, the span with required will show when a required value is missing, and the span with invalid will show when a value the user types doesn’t match the pattern.

A.4.6. entrytemplate.html

The following listing shows your template. As with the other two MV* examples, this file is a snippet of HTML (also referred to as a partial or fragment).

Listing A.22. entrytemplate.html

Like Backbone.js, placeholders mark where the framework will insert the data. You don’t have to worry about writing any template instructions in your code, though. This is another facet of the process that’s magically taken care of by AngularJS.

Finally, the following listing illustrates the controller for your application.

Listing A.23. controllers.js

Remember that with AngularJS, your $scope is similar to a ViewModel.

A.5. Summary

The sample code in this appendix shows you not only how to approach your own POCs but also how differences in philosophies change the balance of where more verbosity lies, when it comes to model-to-view translation: the HTML or the JavaScript.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset