HTML5, JavaScript, and CSS - Java EE and HTML5 Enterprise Application Developmentb (2014)

Java EE and HTML5 Enterprise Application Developmentb (2014)

CHAPTER

5

HTML5, JavaScript, and CSS

The increase in development and availability of HTML5 applications for just about every type of device, from desktop computers to smartphones, and everything in between, has brought much-needed scrutiny to how HTML5 applications can best be used in the enterprise application space. In this chapter you will learn how to create a pure client-side HTML5 application that will consume and interact with the REST, Server-Sent Events (SSE), and WebSocket Java web services that you learned how to write in previous chapters. You will see how to implement the Model-View-ViewModel (MVVM) architecture pattern through the use of the JavaScript library Knockout.js. You will also be shown the basics of responsive design techniques using CSS3 media queries to dynamically change the UI layout when the application is displayed on devices of different sizes. Finally, you will be shown the basics of working with Syntactically Awesome StyleSheets (SASS) and the Sassy CSS (SCSS) syntax for managing more complex CSS file sets in an enterprise application.

HTML5 Project Setup

To get started, create a new HTML5 project using NetBeans IDE:

1. Choose File | New Project.

2. As shown next, select HTML5 in the Categories pane and then select HTML5 Application in the Projects pane. Click the Next button to enter details about your project.

image

3. For the second step of the wizard, enter BookClub in the Project Name field and click Next again.

image

4. In the next step of the wizard, Site Template, you are given three options from which to choose how you want to configure your new project. For this example, you are going to base your new project on a template provided as part of this book. This template will provide the foundation for the information covered later in this chapter. So, as shown next, click the Select Template radio button, click the Browse button, and go to the location of the BookClub.zip file. Click the Next button after you have the BookClub.zip file selected in the Template field.

image

5. The last step in creating your new project is to add any other JavaScript libraries that you may need. The list of JavaScript libraries, shown partially in the following image, is pulled directly from the Content Delivery Network for JavaScript (CDNJS) repository and can be updated at any time by clicking the link under the library list. If you need to add more libraries at a later time, go to the Project Properties dialog in NetBeans IDE and select the JavaScript Library Manager option. For now, all of the JavaScript libraries that you will need for this project are included in the template. Click the Finish button to create the project.

image

Listing 5-1 shows the index.html file that the wizard loaded from the template for your project. As you can see, the <title> element identifies the name of your project. Also note how the CSS and JavaScript libraries are referenced: The <link> and <script> tags in the <head> section tell the browser to load the designated files when the index.html document file is run.

image

Listing 5-1 Setting Up CSS and JavaScript References

image

image

Notice that the last line of the <head> section includes an extra <script> element that references a JavaScript file called app.js. This file contains JavaScript code that is specific to this application. If you were starting this project from scratch and needed to add this file, you would right-click the js node in the project navigator, as shown here, and select New | JavaScript File.

image

A dialog will be shown as in the following illustration. Set the file name to app and click Finish.

image

You can now run your project, and everything should load in the browser without errors.

REST

When working with a REST service, you first will want to identify the REST APIs that you have available to work with. This information is most often found in the developer documentation for the specific REST service. For this chapter, you are going to use the REST service and the persistence APIs that you created in Chapters 2 and 3 of this book. The code for those two chapters is available as part of the book for you to load and run from your installation of the NetBeans IDE. If you are running from a local machine, the URL will belocalhost:8080/sahara/webresources.

The APIs that you have available are shown in Listing 5-2.

image

Listing 5-2 Available REST APIs

image

image

The APIs shown in Listing 5-2 provide the following functionality:

image /books returns a list of all books in the database.

image /books/{id} returns the details for a specific book. The {id} of a book is its ISBN (International Standard Book Number).

image /books/{id}/authors returns all of the authors for a specific book.

image /authors returns a list of all authors.

image /authors/{id} returns the details for a specific author. Each author has a unique ID in the database.

image /authors/{id}/books returns a list of all books that a specific author is associated with.

The easiest way to learn how to use a particular REST service is to write a CRUD application that exercises the APIs required to create, read, update, and delete data. You will use the APIs in Listing 5-2 to do just that in this section. All of the code discussed in this REST section is found in the app.js file of your project.

Read, Using the GET Request (R of CRUD)

You’re going to start with the code shown in Listing 5-3 to perform a read of the available data.

image

Listing 5-3 jQuery getJSON Method Call

image

image

This is a very simple call that uses the jQuery alias of $ and calls the .getJSON method. The call takes two arguments: the URL of the REST API, and a callback function to do something with the data that is returned. Listing 5-3 uses .getJSON() to make the call, so it requests the data to be returned as a JavaScript Object Notation (JSON) object. Because the JSON data format is syntactically identical to the code used to create JavaScript objects, it can be parsed directly from the JavaScript language without the need for any additional parsers or processors.

image

NOTE

image

You’ll notice that the callback function only works against a successful request. If you need to do error handling, you will want to use the full syntax similar to what is shown in Listing 5-7 later in the chapter.

You’ll also notice that the URL in this example is being set using a variable, self.serviceURL, instead of a string for the URL itself. This is because this line is actually part of a larger function called a ViewModel. This is part of the Knockout.js Model-View-ViewModel (MVVM) architectural pattern that you read about in Chapter 1. Listing 5-4 shows the full ViewModel code.

image

Listing 5-4 Example ViewModel

image

image

Let’s walk through what each line provides:

image function booksViewModel() { var self = this; The current object in JavaScript is often referenced by a pseudo variable called this. The major problem with always using this to reference variables in a function is that the scope of this can change depending on how the function is called. You want to bind a local variable to the current this object so that the same object can still be referenced no matter how the function is called later in your code.

image self.serviceURL = serviceRootURL + "/books"; This line sets up the URL that will be used to make the actual REST API call. Since all of the REST API calls have the same hostname and application path, a global variable has been set to contain the part of the URL that is always the same. One thing to keep in mind while working with REST service calls in development mode is that the hostname may vary as you test your code from different devices. For example, using “localhost” as the hostname will work fine for any browser that you would use to connect to the REST service that you have running on the same machine. However, if you were to try to test your code from, say, a tablet and connect to the IP address of your development machine, the JavaScript call to “localhost” would fail. For this reason, you will see at the top of the same application code that the serviceHostname variable is being set to serviceHostname = window.location.hostname; and the final serviceRootURL variable is created using this dynamic value.

image

image self.Books = ko.observableArray([]); This shows your first use of a Knockout.js method. You are assigning the variable self.Books to a Knockout observableArray and initializing it with an empty array. The use of Knockout observables and observableArrays is the key to setting up two-way binding between the data at the Model layer of your MVVM architecture and the View layer. You’ll see a bit later in this chapter how this observableArray is used.

image $.getJSON(self.serviceURL, function(data) { This line was already presented in Listing 5-3 and discussed thereafter, but the variables should make a little more sense at this point. The callback function is processed when a successful request is returned. The data object contains the data that is returned from the server as part of the response object. Because you are using $.getJSON(), the data returned is a JSON object.

image var mappedBooks = $.map(data, function(item) { This line is using the jQuery .map() utility method to take each top-level element in the returned JSON object and add it to an array. Each item is created as a new bookModel object before it is returned from the .map() method.

image return new bookModel(item); In the end, you have an array that contains a collection of models. You’ll find out more about the Model object when Listing 5-5 is discussed a bit later.

image self.Books(mappedBooks); This final line sets the value of the predefined observableArray self.Books to contain the collection of Model objects. You will see how this object is used when you bind the ViewModel to the View layer.

The Model represents one instance of your data. It’s easiest to think of this as a single record in your database, or a single row of a table. In Listing 5-5 you can see that each field in your Model is assigned to a Knockout observable variable. This lets you take advantage of the two-way binding that Knockout.js is known for. Two-way binding means that when the value of an observable or observableArray is changed, either programmatically or from end-user data entry, the Knockout libraries automatically update all of the elements that are bound to it. You do not have to worry about setting up listeners for every variable that is used in your View layer. (This will become clearer when you get to the discussion of the View layer a little later in the chapter.) In the last line of the Model, you will notice that you not only assign each field from the data set, but you can also define your own variables that will be used in your View layer. In the example application, the book cover photos are provided already and are named after the ISBN code for each book. In Listing 5-5, you can see that the variable this.coverImage is being assigned to the value ofbookData.isbn so that it can be used later to load that specific image in the HTML code.

image

Listing 5-5 Example Model

image

image

Now that you have your Model and ViewModel created, there is one last piece to the puzzle. Listing 5-6 shows the HTML code for the View layer. This is included in the main.html file of your project.

image

Listing 5-6 View Example

image

image

Let’s break down this code example:

image <article id="bookInfo"> This element represents a simple semantic element that helps define the structure of the larger page. However, the id attribute is important because it is what will be used by the Knockout.js applyBindings() method to bind this section of the HTML View to the ViewModel that you created previously.

image <div class="item active" data-bind="foreach: Books "> This line is the beginning of the list of books that will be rendered. This <div> will contain the layout and styling for one Model. If this were a table, this would be the layout and styling for one row of data. The key attribute isdata-bind. This uses the Knockout foreach: binding to render everything contained inside this <div> for each item that is in the object “Books.” Recall from the discussion about Listing 5-4 that you set the value of Books to the array of Models returned from the REST call.

image <div id="bookCoverPhoto" class="bookCoverPhoto col-md-3" data-bind="click: getBookDetails"> This <div> is the container for the book cover image and its caption. It has a Knockout click: binding bound to it so that you can drill down in your page to get more details about this specific book. You will see more about getting and displaying the book details a little later, as this details page will be used for the update and delete actions of your CRUD application.

image The next three lines set up the contents that will be displayed for each book. The cover image, the caption, and the ISBN for each book are bound to their own HTML elements. There are two items of note in this code:

image data-bind="attr: {src: 'img/'+coverImage()+'.jpg'}" In this data-bind attribute, the Knockout observable coverImage is being called as a method instead of just referencing the variable as you have seen done in all of the previous bindings so far. You do this when you want to get the actual value of the variable and not a reference to the function that Knockout uses for the two-way binding. This breaks the two-way binding for this reference, but in some cases, that is fine. In this case, you are just using the variable to dynamically get the name of the coverImagefile. You do not plan on changing this name in other parts of your code, so you don’t need to have it set up as a two-way binding. You will see a better example of why you would do this later in the chapter, when you get to the Update section of the code.

image class="hidden" data-bind="text: isbn"> In this code line, the value is being bound to the ISBN value for this particular book. You don’t actually need to use this value as part of the current View, but you do need this value when you get the details for this book. By setting the CSS style class to "hidden", the actual <div> is not rendered, but the ISBN value will be available as part of the data that Knockout passes to the function as part of the click: binding.

The final result of putting all of the code from Listing 5-3 through Listing 5-6 together will look like Figure 5-1 when you run it in a browser.

image

image

image

FIGURE 5-1. List of all books

Create, Using the POST Request (C of CRUD)

Now that you know how to do the most common task, reading data, with a REST service, you will learn how to add a new record to your data service.

For this example, you will be adding a new book to the database. You will use the same REST API that you used for reading the list of books, /books; however, the request type that you use will be a POST request instead of the GET request that you used previously. Listing 5-7 shows the code for making this POST request.

image

Listing 5-7 Example of AJAX POST Call

image

image

Let’s walk through the code:

image $.ajax({, This is the jQuery method for implementing an asynchronous HTTP request (AJAX).

image url: serviceRootURL + "/books", This line sets the URL that you will make the REST call to.

image type: 'POST', This line sets the request type to POST. This tells the REST service that you want to add this new data to the database.

image data: JSON.stringify(json), The data that you will pass into the request is going to come from a form, which you’ll learn how to create in a little while. Since the REST service is expecting to receive data as a JSON object, you will use the utility method .stringify()provided by the JSON object to convert the JavaScript object into a valid JSON object.

image dataType: 'json', This is the type of data that you are expecting back from the server in response to this request.

image contentType: 'application/json', This tells the server what type of data you are sending to it in this request.

image success: This is the callback function that is used if the request is successful.

image error: This the callback function that is used if the request fails.

image

TIP

To test your error callback function, try sending a request with the same value for a field that is required to be unique. In the case of the sample application, try adding two books with the same ISBN.

The HTML code shown in Listing 5-8 includes a Knockout click: binding that displays the dialog in which to enter the information about the new book that you want to add to the database.

image

Listing 5-8 click: Binding to Show New Book Dialog

image

image

This code is straightforward. The Knockout click: binding is calling a function called showAddDialog. This function is using the modal dialog functionality provided by the Twitter Bootstrap framework (with which you initially created the project) to display a modal dialog. This dialog contains the form that gathers the details for new books being added to the database. Listing 5-9 shows the HTML for the dialog. The one line of code that displays the modal dialog is

image

image

image

Listing 5-9 New Book Modal Dialog with Form

image

image

image

image

This is a really large piece of code, but almost all of it is just HTML layout and styling. Let’s discuss the lines that provide the functionality:

image <form class="form-horizontal" data-bind="submit: addBook"> This is the Knockout submit: binding that tells Knockout to pass all of the form elements to the addBook function when a submit is performed.

image <button class="btn btn-primary" type="submit">Save< /button> By setting the type attribute of this button to "submit", it will fire the submit event when it’s clicked, causing the initial submit: binding to be invoked.

When the form is submitted, it will call back to the addBook function that contains the REST call that you reviewed previously. The only thing not shown in the previous example is how the data was formatted into the JSON object that was passed as part of the request. Listing 5-10 shows you how the JSON data is created using the form elements passed in by the Knockout submit: binding.

image

Listing 5-10 Example Parsing Form Data to JSON Object

image

image

The data passed into the addBook() function contains an array of the HTML elements, and you will assign the value, from those elements that you need, to the fields that are expected by the REST service. In this case, the array includes the five fields shown in Listing 5-10. When you are sending data to the REST service, you have to know in which format each field is expecting its value to be sent. For example, when the publishedDate field is being set, the REST service expects a Date to be sent for this field, so you will need to transform the data from the form into the appropriate format.

Data validation can be done in two ways at this point. You can use built-in validation functionality from the client-side frameworks, such as Twitter Bootstrap, to make sure that the data entered into the form is in the proper formats before the submit event is fired, and/or you can perform validation on the data as you are assigning it to the JSON object. The sample application provided doesn’t perform any client-side validation. It does, however, provide a hint on the Published Date form field to help the end user know which format is expected for this field:

image

image

Knowing which specific HTML element contains the data that you want to assign to a specific field in your JSON object may be tricky if you have also included elements in your form that you don’t want to use. NetBeans IDE provides a JavaScript debugger that is very useful for determining what is what in the data object returned to the function. Figure 5-2 shows an example of how the debugger can be used to determine which elements you need to use to achieve the code shown in Listing 5-10. Clicking in the left-hand gutter of your JavaScript file will place a breakpoint on that line, as shown for line 141 in Figure 5-2. When the form is submitted in the browser, the IDE will stop at this line. You can hover over the data variable to see the value of it in the tooltip. Expanding it will show each of the elements in the data object and their values. Of course, you can also see this debugger information in the Variables window at the bottom of the IDE if you don’t want to use the tooltip approach.

image

image

image

FIGURE 5-2. NetBeans JavaScript debugger example

Now that you have submitted the request to add the book to the REST service, you want to finish any changes to the View layer that may be affected by the result of the POST request. Taking another look at Listing 5-7, you will see the two callback functions for success and error. If there is an error, you should do something to inform the end user that the request didn’t work. In this example, an alert is called with the error status and any text that the REST service returned as a result of the error:

image

image

In the case where the request is successful, you will want to add the same JSON data that you sent to the server to the existing Knockout observableArray that your View is bound to. In Listing 5-7, the line of code booksVM.Books.push(new bookModel(json)); creates a new Model object out of the JSON data and then pushes it into the observableArray booksVM.Books(). Because Books is a Knockout observableArray, just adding the new Model object will cause an event to be fired, and the HTML that is bound to the foreach: binding will automatically update itself.

Update, Using the PUT Request (U of CRUD)

The process for updating a record is about the same as the process for creating a new entry, as covered in the previous section. First you will want to see the details for the book that you are going to edit. Listing 5-11 shows how the HTML is coded to list all of the books in your service.

image

Listing 5-11 Example of Knockout foreach: Binding

image

image

The Knockout binding of foreach: is used to display each item in Books with the same look and feel. Books is the Knockout observableArray from your ViewModel. The most important element in the HTML code is actually a hidden <div> that contains the ISBN for each book. The ISBN is a unique ID for each book, which will allow you to make another REST call to get the information about one specific book. Notice that each of the <div id="bookCoverPhoto"> elements for the book’s cover image, title, and ISBN has a click: binding to the function getBookDetails(). This function will load the page that shows all the details about a specific book. Listing 5-12 shows this function.

image

Listing 5-12 getBookDetails Function

image

image

Let’s take a look at this code:

image The first line simply sets a local variable, bookData, to contain the data from the page that you just left.

image The entire sample application that you have been working with so far is designed as a single-page application. This means that the application doesn’t change URLs and load a new page when a request is made, but rather loads a new page fragment, or template, into a specific section of the larger page. In the case of this application, the index.html page has been designed to have the header and footer code and a single <div> in the body of the page to contain all the other page templates. This <div> has an ID of mainpage. Because Knockout allows you to have only one binding to a DOM element at one time, you need to clear out the existing binding and add the new bindings as you load the new page template.

image After ko.cleanNode($('#mainPage')); clears out the mainpage DOM element, $('#mainPage').load ('bookDetails.html', function() { loads the new page template into that same DOM element.

image In the callback function for the .load method, you will set the new ViewModel and bind that ViewModel to two separate DOM elements. The first is the container for all of the individual book details, and the second is the container for the modal dialogs. The dialogs will be used for updating the details for this book and as a confirmation of whether to delete the book or not.

Once you call the Knockout applyBindings() call, the bookDetailsViewModel() ViewModel will be processed. Listing 5-13 shows this ViewModel code.

image

Listing 5-13 Example of bookDetailsViewModel Function

image

image

When you created the new instance of the bookDetailsViewModel() in the getBookDetails() function, you passed in the bookData variable. This means that you have all of the information that you need for that specific book and can assign those values to Knockout observables for use in your View. However, the Author information is not included in that book data. The Author details are kept in a separate table of your database. There is a REST API for getting the Author details for a specific book, though. In the fourth line of code in Listing 5-13, you can see that you are setting a variable called self.serviceURL to point to that REST API.

Further down in Listing 5-13 you make a getJSON() call to get the Author details:

image

image

In your database, Author is not a required field, so this API could return successfully and still not contain specific information. Because of this, the code is testing to see if the length of the returned data is larger than zero; if it is, the code sets the Knockout observable for the author’s name to be a concatenation of the first and last name returned. If the returned data doesn’t contain any information, the code sets the author to Unknown.

Now that you have loaded the new page template for book details and have pulled in the appropriate data to populate the page, you can take a look at how to allow your end user to edit the book data.

Listing 5-14 shows that at the top of the book details template HTML code there is a knockout click: binding added to an icon for Edit and another icon for Delete. Both of these functions do nothing more than load and display the dialogs for their specific purposes.

image

Listing 5-14 Example of Icons for Edit and Delete

image

image

Let’s look at Listing 5-15 to see how the updateBookDialog dialog is handled first.

image

Listing 5-15 Partial Edit Dialog

image

image

Like the addBookDialog dialog created in Listing 5-9, this is a Twitter Bootstrap modal dialog. There are two important things to note in Listing 5-15. First, you don’t need to pass in any information to this dialog directly. Each field is bound to the existing Knockout observable that was also used to display the book details in the main page.

image

NOTE

Notice that all <input> elements use a value: binding while other DOM elements use a text: binding to get their content. When working with Knockout bindings, it’s important to bind the proper content attribute for each type of DOM element. For example, if you set the content of a DOM element by using the value attribute, then you would use the value Knockout binding.

Second, notice that the Knockout observables are being called as a function in all of the form element bindings: data-bind="value: isbn()". This is very important. Because of Knockout’s two-way binding, if you were to attempt a binding to the observable directly, as soon as you changed it in the form field, it would update in the book details page. You don’t want this to happen immediately, just in case the end user clicks the Cancel button instead of submitting the changes. By calling the observable as a function, you get the actual value of the variable instead of the observable object.

Listing 5-16 shows how the form is processed and the actual update is performed in the REST API.

image

Listing 5-16 Submitting Update

image

image

Let’s walk through this code to examine what is going on:

image The JSON object that will be passed into the REST API call is being created using the form data passed when the Knockout submit: binding is triggered.

image The same AJAX call that you used to add a new book is used again to update a book. However, the type attribute used is now PUT instead of POST as was used for the add method.

image You have the same success: and error: callback functions as well. The error: function is exactly the same as before.

image After the update has been completed, the application returns to the main page. The success: function closes the updateBookDialog dialog and then waits for that close to be completed. It then calls the loadDefaults() function, which resets the ViewModel and correct bindings back to the main page.

Delete, Using the DELETE Request (D of CRUD)

The setup for the update process was a little long, but that setup also enabled the delete functionality. The confirmDeleteDialog dialog is displayed in the same way that the updateBookDialog dialog was, with a call to a showDeleteDialog() function. Listing 5-17 shows the REST API call that is needed to perform a delete.

image

Listing 5-17 Example of DELETE REST API Call

image

image

After the user clicks the Yes button in the confirmDeleteDialog confirmation dialog, the deleteBook(data) function is called and the current Books details are passed to the function. To delete a record using a REST API, you need to have a unique ID for the record you want to delete. In the example application’s case, this is the ISBN value.

Let’s take a look at the code in Listing 5-17:

image The JSON object that will be passed to the AJAX call needs to contain only the unique ID for the record you want to delete. Notice that, once again, you are setting the actual value of the observable, and not a reference to the observable object.

image A local variable is set to the current ISBN value so that the ISBN value can be used in a comparison function later.

image A local variable is set to the Books observableArray so that you can remove the Model entry if the REST API call is successful.

image The URL is set in the AJAX call to use the REST API that will get the exact book that you want to delete.

image The type attribute is set to DELETE.

image The dataType and contentType are both set to JSON so that the service knows that is what you are sending and expecting back.

image As with all the other calls, there are success: and error: callback functions. The error: function is exactly the same as before.

image The success: function uses the Knockout utility function of arrayFirst() to find the first match in the observableArray. Once that is found, it returns a reference to that Model so that it can be removed using another Knockout utility function called arrayRemoveItem().

image Just as you did with the updateBookDialog dialog, the confirmDeleteDialog dialog is closed, and then once the close is completed, the loadDefaults() function is called to reset the ViewModel and load the main page.

Server-Sent Events (SSE)

Server-Sent Events is sometimes referred to as push technology because the data flow is in one direction only. Everything comes from the server. The browser chooses to attach to the SSE stream to have information sent to it whenever new information becomes available. The example application has an Events section set up that will display data coming from an SSE stream. Knockout two-way binding is really helpful with the displaying of the data since you can set up the binding between the incoming data and a specific DOM element and just let Knockout update whenever the observable is changed by the new data coming in. Listing 5-18 shows how you connect to the SSE stream and which functions are used to work with the stream and its data.

image

Listing 5-18 Example of Server-Sent Events

image

image

Server-Sent Events is a new feature introduced in HTML5. Some browsers do not support this feature at the time of writing.

image

TIP

Currently, no versions of Microsoft Internet Explorer support Server-Sent Events.

Before continuing with the initialization code, it’s a good idea to perform the following check to make sure the browser provides SSE support:

image

image

If the browser doesn’t understand the type of EventSource, then it doesn’t support SSE, in which case you can display an error message.

Let’s walk through the code in Listing 5-18 to see what each section does:

image The first few lines set the defaults for your Knockout observables. These observables are bound to the SSE section of the HTML code.

image Inside the if statement that is checking to make sure the browser supports Server-Sent Events, you create a new EventSource object and assign it to the self.source variable. The URL that is used to make the connection is for the SSE stream that you wrote in Chapter 3 of the book:
image

image Once you have the EventSource object, there are three events you can listen for and then perform the appropriate actions:

image onopen: This event is triggered on the initial opening of the connection.

image onmessage: This is triggered each time the server sends a message to the browser. This is the main event that you will want to work with.

image onclose: This is triggered when the connection is closed for any reason.

image In Listing 5-18, you are setting the local variable message to a string stating that the connection was opened. You are then assigning that value to the Knockout observable sseData, which will be displayed inside the SSE HTML section that is bound to the same observable. Because you are including HTML markup in the message, the Knockout binding on the HTML page is actually the html: binding instead of the plain text: binding:

image

image In the onmessage function, you want to lay out the message that goes into this area by appending the most recent message from the server onto the end of the existing messages. You do this by setting the local variable message to the actual value of the observable and then appending the incoming event.data. After generating the new appended message, you set the self.sseData observable to the new value.

image If the connection is closed for whatever reason, you set a message stating that the connection is closed and set the same self.sseData observable to show that message.

image When the original observables for self.displayMessage and self.displayError were initialized, they were set to false so they would not show these areas while the page is loading. Now that the connection has been established and a message has been set, you can set the observable to true, which will display the Message area while still leaving the Error section hidden.

image Finally, there is a small X icon in the View layer of the Event section that has been bound to the closeSSE() function. When the icon is clicked, the current SSE stream closes and an appropriate message is displayed.

WebSocket

Using the WebSocket API in JavaScript and connecting the resulting data to an HTML view layer was covered in Chapter 4. If you haven’t read Chapter 4 yet, the basic approach to setting up the connection to the service is the same as what you just learned with Server-Sent Events. However, with the WebSocket API, you can also send data back to the server. One of the biggest advantages of using a WebSocket connection instead of REST or some other type of protocol is the significant reduction in the amount of traffic that you send over the wire when you use the WebSocket protocol. The connection between the client and server is set up once and then maintained until the connection is closed either via the API or via the client closing. With other protocols, you are required to go through the connection setup each time you want to send or receive data from the server.

To get a full description of the new WebSocket protocol, please read Chapter 4.

Responsive Design

The concept of responsive design addresses the need to design your web application such that it renders its layout appropriately in response to the screen size of the device on which it is being displayed. The application View layer needs to respond to this change in view size by changing the layout, or amount, of the content that is rendered.

The CSS2 specification provided the capability to use different CSS rules dynamically based on the media type. The most common of these types were (and continue to be in CSS3) screen and print. The CSS3 specification expands this capability with the introduction of media queries. A media query is made up of a media type and zero or more expressions. These expressions check for specific conditions of media features. A good example of this is an expression that checks for the current size of the display screen:

image

image

Through the use of media queries and a pattern of CSS classes defined as a grid layout, you can make your application respond to changes in the display screen.

The grid layout pattern is something that you can design yourself, but it’s much easier to use an existing grid framework. Thus far, you have been using the Twitter Bootstrap framework to show modal dialogs and to help with the display of buttons and general form layout. Twitter Bootstrap also comes with a grid layout as part of its CSS files. The most common grid style in use is a 12-column grid. You can define different sections of your layout into any number of columns that would add up to the largest width of 12. For example, you could have three sections that were each set to be four columns wide. In the sample application’s model dialog for adding a new book, for example, the dialog uses a nine-column width for all of the form elements, while the label elements use three columns. Listing 5-19 shows how this looks in part of that dialog HTML code.

image

Listing 5-19 Sample Grid Layout HTML

image

image

Notice the style class of class="col-md-9" in each of the input <div> elements and the class="col-md-3" style class for the labels. The combination of these style classes and media queries is what makes responsive design possible.

Let’s take a look at the CSS code in Listing 5-20.

image

Listing 5-20 CSS Example for Grid Layout

image

image

The two sections of Listing 5-20 follow the style class for .col-md-9 through to different media queries. The first section covers the situation in which the browser size doesn’t fall into any specific defined media query. The second section introduces the media query for the situation in which the browser size is set to min-width:992px. Any style rules that you place inside a specific media query will override any previous values for the same rule while the media query expression is true. If you don’t override a rule, the existing rule will continue to be used. In the code shown, you are not overriding an existing value, but rather setting new rules for width: and float:. Notice that the width is being set to a percentage instead of a specific numeric value. This will cause the nine-column grid to maintain its relative size as the browser is resized, but it still remains within the range that this media query has defined.

Figure 5-3 shows the CSS Styles property window in NetBeans IDE. Selecting an element in the DOM navigator will show that element’s current CSS styles and rules, including whether the browser is currently resized to fit within a specific media query.

image

image

image

FIGURE 5-3. NetBeans CSS Styles window

Syntactically Awesome Stylesheets (SASS)

For most HTML5 applications, you may be able to get away with using a single CSS file that is reasonably constructed and easy to maintain. However, as the applications grow and become more complex, the CSS becomes just as complex. Historically, maintaining very large CSS files has been extremely difficult. Even if you broke the files down into smaller files and referenced each file in your HTML, you had to remember to place the files in the proper order because CSS files are loaded by the browser with the order they are listed, but each file will overwrite any previously set style classes. A concept of last in wins. If you happened to have the same rule defined in multiple files, the file in which the rule would take effect depended on the order in which the files were listed in your HTML code. In 2007, SASS was introduced by a developer named Hampton Catlin. The development of SASS is continued today by Nathan Weizenbaum and Chris Eppstein.

The original dynamic stylesheet language was designed to be similar to the Haml (HTML abstraction markup language) programming language in its use of an indentation syntax style. Referred to as indented syntax, this style really doesn’t look or feel like CSS at all. This syntax is most often referenced with a file extension of .sass. A newer syntax has been created called Sassy CSS, or SCSS, which is designed to look and feel exactly like CSS itself—so much so that any valid CSS code is also a valid subset of SCSS. This syntax is most often referenced by a file extension of .scss.

SASS works as a preprocessor of the SASS or SCSS files and compiles these separate files into one or more CSS files that you can reference in your HTML code.

Listing 5-21 includes two link references to stylesheets. The first is for the Bootstrap framework that you used as the basis of the My Book Club sample application, and the second is the reference to the application’s specific styles. The latter file, named responsive.css, is a CSS file that was generated as a result of the SASS preprocessing of four separate SCSS files. These files are shown in Figure 5-4.

image

Listing 5-21 Referencing the CSS File

image

image

As you can see in Figure 5-4, the responsive.scss file contains only three lines of real code. It imports the other three SCSS files in the order in which you want them to be assembled. The other three files are broken out to contain the classes and rules specific to a certain media query type: in this case, Desktop, Tablet, and Handheld. This separation is just one example. Your individual projects will dictate how you determine what is the best way to break up your CSS into more manageable sections.

image

image

image

FIGURE 5-4. Listing of SCSS and generated CSS files

NetBeans IDE makes it very easy to work with SASS in your projects. Right-click your project name in the Navigator and select the Properties menu option. In the Project Properties dialog, select the CSS Preprocessors option, as shown in Figure 5-5. For this project, the IDE is being instructed to look for the SCSS files in a directory named /scss and to output the compiled files into the /css directory.

image

image

image

FIGURE 5-5. CSS preprocessor settings

image

TIP

When working with SASS, if you start the name of an SCSS file with an underscore, that will instruct the compiler to not compile that specific file into its own CSS file. This is useful when you have multiple smaller files that are being imported into one larger file and you only want the larger file compiled to a final CSS file.

If you have not already done so, you can install and manage the executable for SASS by clicking the Configure Executables button at the top right of this dialog. The Global IDE options dialog for working with CSS preprocessor executables is shown in Figure 5-6. You’ll notice that if you don’t know where to download the executable from, there is a link provided that will take you to the proper location.

image

image

image

FIGURE 5-6. Global IDE properties for CSS preprocessors

You will also notice that NetBeans IDE supports another type of CSS preprocessing called LESS. This is a similar approach to SASS and was designed by Alexis Sellier in 2009. As with many open source projects, one project may influence another and vice versa. In this case, SASS was designed in 2007, which influenced the development of LESS in 2009. The introduction of LESS then influenced the new SCSS syntax used in SASS. Both stylesheet languages have their advantages and disadvantages. As far as NetBeans IDE is concerned, the setup and functionality are the same for either one.

Now that you have seen how you can work with SCSS files using NetBeans IDE, you are ready to take a look at the code. Listing 5-22 presents a very simple example of nesting, and then displays the resulting CSS in the generated file. All of the related rules for the #footerContent class are nested inside the root style in the SCSS file. After the CSS is compiled, the generated CSS contains separated rules.

image

Listing 5-22 SCSS Nesting and the Resulting CSS Code

image

image

Summary

In this chapter you’ve learned how to work with an HTML5 project within NetBeans IDE and how to consume and interact with a REST web service to perform CRUD functions. You connected to a Server-Sent Events (SSE) stream using JavaScript, and then used the data from that stream to update an events window in the BookClub application. You were introduced to the Model-View-ViewModel (MVVM) architectural pattern through the use of the Knockout.js JavaScript library. You’ve also learned the basics of responsive design using a grid layout and media queries provided by the Twitter Bootstrap framework. Finally, you briefly looked at how you can use a CSS preprocessor to manage large and complex CSS implementations.

The BookClub application that was used throughout this chapter was intentionally left unfinished in the area of the Author details. This is so that you can use the tasks you learned while setting up the Book details to add the same type of functionality to the Author section of the application.

image