To read the article online, visit http://www.4GuysFromRolla.com/articles/112410-1.aspx

Updating My Online Boggle Solver Using jQuery Templates and WCF

By Scott Mitchell


Introduction


With WebForms, each ASP.NET page's rendered output includes a <form> element that performs a postback to the same page whenever a Button control within the form is clicked, or whenever the user modifies a control whose AutoPostBack property is set to True. This model simplifies web page development, but carries with it some costs - namely, the large amount of data exchanged between the client and the server during a postback. On postback the browser sends the values of all of its form fields (including hidden ones, like view state, which may be quite large) to the server; the server then sends back the entire contents of the web page. While there are some scenarios where this amount of information needs to be exchanged, in many cases the user has performed some action that requires far less information to be exchanged. With a little bit of forethought and code we can have the browser and server exchange much less data, which leads to more responsive web pages and an improved user experience.

Over the past several weeks I've been writing an article series on accessing server-side data from client script. Rather than rely solely on forms and postbacks, many websites use JavaScript code to asynchronously communicate with the server in response to the page loading or some other user action. The server, upon receiving the JavaScript-initiated request, returns just the data needed by the browser, which the browser then seamlessly integrates into the web page. There are a variety of technologies and techniques that can be employed to provide both the needed server- and client-side functionality. Last week's article, Using WCF Services with jQuery and the ASP.NET Ajax Library, explored using the Windows Communication Foundation, or WCF, to serve data from the web server and showed how to consume such a service using both the ASP.NET Ajax Library and jQuery.

In a previous 4Guys article, Creating an Online Boggle Solver, I built an application to find all solutions in a game of Boggle. (Boggle is a word game trademarked by Parker Brothers and Hasbro that involves several players trying to find as many words as they can in a 4x4 grid of letters.) This article takes the lessons learned in Using WCF Services with jQuery and the ASP.NET Ajax Library and uses them to update the user interface for my online Boggle solver, replacing the existing WebForms-based user interface with a more modern and responsive interface. I also used jQuery Templates, a JavaScript-based templating library that is useful for displaying the results from a server-side service. Read on to learn more!

A Quick Overview of My Boggle Solver Application


My immediate and extended family enjoys playing games, and one of the favorites is Boggle. Boggle is a word game trademarked by Parker Brothers and Hasbro that involves several players trying to find as many words as they can in a 4x4 grid of letters. At the end of the game, players compare the words they found. During this comparison I've always wondered what words we may have missed. Was there some elusive 10-letter word that no one unearthed? Did we only discover 25 solutions when there were 200 or more? I decided to answer these questions and in April 2008 I created an online Boggle solver using ASP.NET. The Boggle solver recursively explores the board and locates (and displays) all available solutions. With this nifty little web page, at the conclusion of each Boggle game we can see what words we missed. I wrote about creating this application in an article titled Creating an Online Boggle Solver. You can see a live demo in action at www.FuzzyLogicInc.net/Boggle.

The Boggle solver is a standard WebForms application; its user interface consists of 16 TextBox Web controls, a DropDownList control, a couple of Button Web controls, and a ListView. To solve a particular Boggle board, a user would visit the page and enter each of the 16 letters from the Boggle board into the 16 TextBox controls on the page and choose the minimum number of letters each solution must contains from the DropDownList. (The rules of Boggle require all words must be at least 3 letters long; my family usually plays with a 4 letter word minimum.) After entering the letters and choosing the minimum number of letters per solution, the user clicks the "Solve Puzzle" button. On postback, the server recursively searches the board for solutions, building up a collection of found words. This collection is bound to the ListView control, displaying the solutions in a three-column table.

The screen shot below shows the Boggle solver application after the user has entered the letters of the board and clicked the "Solve Puzzle" button. Note that they are interested in any solutions with 3 letters or more. The user could, optionally, choose a different minimum number of letters from the DropDownList. (The DropDownList has the hard-coded values 3, 4, 5, and 6.)

The Boggle Board screen.

Whenever the user clicks any of the Button controls on the web page the browser performs a postback, which sends all of the form-field values back to the server. The server then re-renders the page and returns the entire page's markup. For example, after entering a Boggle board, choosing a minimum number of letters per solution, and clicking the "Solve Puzzle" button, the browser sends not only the 16 letters and the minimum number of letters per solution, but it also sends the page's view state, event validation, and other markup to the server. Using a tool like Fiddler you can see that the total amount of data sent to the web server exceeds 2,000 bytes, of which the vast majority is view state. Likewise, on postback the server re-renders the entire page and returns that entire markup to the browser even though only a portion of the web page needs to be updated (namely, the list of solutions).

We can improve the user experience by having the client and server exchange only the needed information. For the "Solve Puzzle" button this means having the browser send just the 16 letters and the minimum number of letters per solution and having the server return just the solutions. This article shows how to implement such a user interface. The demo available for download at the end of this article includes both the original WebForms user interface as well as the more responsive, Ajaxy, WCF + jQuery Templates one. Similarly, the live demo at www.FuzzyLogicInc.net/Boggle includes both the original user interface and the Ajax-enabled one.

Creating the Server-Side Solver.svc Service


With the old WebForms-based user interface, the solutions for a given board were computed on postback and displayed in a ListView control. To provide a more responsive user interface, we need the web server to provide some server-side service that, when supplied with the Boggle board and minimum number of letters per solution, returns the set of solutions. To accomplish this I created an AJAX-enabled WCF Service named Solver.svc.

Add a WCF Service named Solver.svc to your website.

As we examined in Using WCF Services with jQuery and the ASP.NET Ajax Library, creating a WCF service creates a .svc file and a code file, where the service's code exists. It also adds a <system.serviceModel> element to Web.config. Next, I added a Solve method to the code file that takes the board and minimum word length values as two inputs and returns an object of type BoggleSolutionsDTO. Also, I decorated the method with the WebGet attribute, indicating the the response should be returned as a formatted JSON string and that the method will be invoked using the UriTemplate ?BoardID={board}&Length={length}.

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Solver
{
   [OperationContract]
   [WebGet(UriTemplate = "?BoardID={board}&Length={length}", ResponseFormat = WebMessageFormat.Json)]
   public BoggleSolutionsDTO Solve(string board, string length)
   {
      ...
   }

}

Recall that the UriTemplate indicates the URL pattern used when calling a particular service method. In this case, anytime a request comes into the URL http://www.yoursite.com/Solver.svc?BoardID=board&Length=length, the Solver service's Solve method is invoked, passing in the values of board and length in the URL into the board and length input parameters in the Solve method. (Here, board is a string containing the 16 letters in the user-entered Boggle board; length is the minimum number of letters required per solution.)

The Solve method returns an object of type BoggleSolutionsDTO. The BoggleSolutionsDTO class is a simple data transfer object class that consists of four properties:

  • BoardID - the board that was solved (a string containing the 16 letters of the board),
  • MinimumLettersForSolution - the minimum number of letters required per solution,
  • TotalScore - the sum of the scores of each solution (solutions with more letters are worth more points),
  • Solutions - an enumeration of BoggleWordDTO objects. Each BoggleWordDTO object indicates the actual solution and how many points said solution was worth.
The Solve method is fairly simple. It starts by ensuring that the board and length input parameters are kosher. Next, it creates a new BoggleBoard object based on the board input parameter. The BoggleBoard class has a couple of methods, the two most germane being IsValid and Solve. The IsValid method returns a Boolean value indicating whether the supplied board was valid; in short, it checks to ensure that a letter was entered for each of the 16 cells that make up the board. The BoggleBoard class's Solve method solves the puzzle and returns an object of type BoggleWordList, which is an enumerable collection of BoggleWord objects that represent the set of solutions in the board.

As the following code shows, presuming the board is valid, the Solver service's Solve method calls the BoggleBoard class's Solve method and packages the solutions into a BoggleSolutionsDTO object, which is then returned and returned. (The below code has some initial error checking and input parameter validation code removed for brevity.)

[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Solver
{
   [OperationContract]
   [WebGet(UriTemplate = "?BoardID={board}&Length={length}", ResponseFormat = WebMessageFormat.Json)]
   public BoggleSolutionsDTO Solve(string board, string length)
   {
      // Create the BoggleBoard
      var bb = new BoggleBoard(
                     letters,
                     board[0].ToString(), board[1].ToString(), board[2].ToString(), board[3].ToString(),
                     board[4].ToString(), board[5].ToString(), board[6].ToString(), board[7].ToString(),
                     board[8].ToString(), board[9].ToString(), board[10].ToString(), board[11].ToString(),
                     board[12].ToString(), board[13].ToString(), board[14].ToString(), board[15].ToString()
                  );

      // Ensure that the board is valid
      if (!bb.IsValid())
         throw new ArgumentException(string.Format("The board '{0}' is not a valid Boggle board: {1}", board, bb.ErrorMessage));

      // Solve the Boggle board
      var solutions = bb.Solve();

      // Populate and return an array of BoggleWordDTO objects
      return new BoggleSolutionsDTO()
      {
         BoardID = bb.BoardID,
         MinimumLettersForSolution = bb.MinimumWordLength,
         TotalScore = solutions.TotalScore,
         Solutions = solutions.Select(s => new BoggleWordDTO()
         {
            Word = s.Word,
            Score = s.Score
         })
      };

   }
}

In addition to adding this code, the Solver.svc file also needs to be configured to use the WebServiceHostFactory class as its factory class. Recall from our discussion in Using WCF Services with jQuery and the ASP.NET Ajax Library that the .svc file's Factory attribute value (or the configuration in Web.config) determines whether the JSON returned from the WCF service is formatted for consumption by the ASP.NET Ajax Library or is returned in its native form. When using jQuery to consume the service, it is easier if the JSON is returned in its native form. For this reason, set the Solver.svc file's Factory attribute to WebServiceHostFactory and clear out the <system.serviceModel> configuration added to Web.config. After making these changes, the Solver.svc file should contain the following markup:

<%@ ServiceHost Language="C#" Debug="true" Service="Solver"
                CodeBehind="~/App_Code/Solver.cs"
                Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>

Building the Ajax-Enabled Boggle Solver User Interface: Collecting the User's Input and Solving the Puzzle


The original, WebForms-based Boggle solver user interface was composed of a number of Web controls - 16 TextBoxes, a DropDownList, three Buttons, and a ListView. For the Ajax-enabled user interface, we do not need these Web controls. Instead, we will just use the appropriate HTML elements. The markup below shows the drop-down list for the minimum number of letters with the hard-coded values 3, 4, 5, and 6, along with the 16 textboxes arranged in a 4x4 table and the three buttons - "Solve Puzzle", "Random Board", and "Clear Board". Note that each textbox has the id value cXY, where X is the row the textbox appears in and Y is the column.

<p>
   Enter the characters into the 4x4 grid below. Use "Q" for the "Qu" tile.<br />
   Return words with at least
   <select id="MinimumLetters">
      <option value="3">3</option>
      <option value="4">4</option>
      <option value="5">5</option>
      <option value="6">6</option>
   </select>
   letters.
</p>

<table>
   <tr>
      <td><input type="text" class="BoggleGridCell" id="c00" maxlength="1" /></td>
      <td><input type="text" class="BoggleGridCell" id="c01" maxlength="1" /></td>
      <td><input type="text" class="BoggleGridCell" id="c02" maxlength="1" /></td>
      <td><input type="text" class="BoggleGridCell" id="c03" maxlength="1" /></td>
   </tr>
   <tr>
      <td><input type="text" class="BoggleGridCell" id="c10" maxlength="1" /></td>
      <td><input type="text" class="BoggleGridCell" id="c11" maxlength="1" /></td>
      <td><input type="text" class="BoggleGridCell" id="c12" maxlength="1" /></td>
      <td><input type="text" class="BoggleGridCell" id="c13" maxlength="1" /></td>
   </tr>
   <tr>
      <td><input type="text" class="BoggleGridCell" id="c20" maxlength="1" /></td>
      <td><input type="text" class="BoggleGridCell" id="c21" maxlength="1" /></td>
      <td><input type="text" class="BoggleGridCell" id="c22" maxlength="1"" /></td>
      <td><input type="text" class="BoggleGridCell" id="c23" maxlength="1"" /></td>
   </tr>
   <tr>
      <td><input type="text" class="BoggleGridCell" id="c30" maxlength="1" /></td>
      <td><input type="text" class="BoggleGridCell" id="c31" maxlength="1" /></td>
      <td><input type="text" class="BoggleGridCell" id="c32" maxlength="1" /></td>
      <td><input type="text" class="BoggleGridCell" id="c33" maxlength="1" /></td>
   </tr>
</table>

<p>
   <input type="button" id="solve" value="Solve Puzzle" />
   <input type="button" id="random" value="Random Board" />
   <input type="button" id="clear" value="Clear Board" />
</p>

This web page uses the jQuery library to communicate with the server-side Solver.svc service, in addition to a variety of other helpful client-side tasks. For instance, when the page is loaded in the browser JavaScript code executes that sets the focus to the top-most and left-most textbox (c00) and adds event handlers to each of the 16 textboxes so that whenever a user enters a letter the focus is automatically switched to the next textbox in the 4x4 grid.

Similarly, the solve button's client-side click event is wired up to an event handler named SolveBoard. The SolveBoard function starts by examining the letters entered by the user and constructing the board string that is required by the Solver.svc service's Solve method. If there are any missing letters or non-alphabetic characters entered in the board, the SolveBoard function displays an appropriate error message. If there are no problems with the input, it makes an HTTP request to the server using jQuery's $.getJSON function. The germane code for the SolveBoard function follows (note that the error handling logic has been removed for brevity):

function SolveBoard() {
    // Make sure all tiles are entered and build up the BoardID value
    var boardId = '';

    $.each($("input.BoggleGridCell"), function (index, value) {
       var row = parseInt(index / 4) + 1;
       var col = (index % 4) + 1;
       var cellValue = $(value).val();

       boardId += cellValue;
    });

    // Go and get the solutions!
    $.getJSON(
                "Solver.svc?BoardID=" + boardId + "&Length=" + $("#MinimumLetters").val(),
                DisplayResults
             );

    // Disable the solve buttons
    $("input[type=button]").attr('disabled', 'disabled');
}

jQuery's $.each function is used to enumerate the collection of textboxes on the page that are assigned the BoggleGridCell CSS class. For each textbox, the value is appended to the boardId property. So, if the entered Boggle board is of the following format:

r e i b
t m f w
i r a e
r h s t

Then the boardId string will be set to reibtmfwiraerhst.

The puzzle is solved by making an HTTP request to Solver.svc?BoardID=boardId&Length=length, where boardId is the boardId value just constructed and length is the selected value of the MinimumLetters drop-down list. Note that the $.getJSON function makes an asynchronous HTTP request, so execution continued immediately following the call without waiting for a response. When a response is returned from the server the DisplayResults will be executed.

The SolveBoard concludes by disabling all of the buttons on the page. This disabling is meant to signal to the user that their request is being processed and to prevent them from clicking the "Solve Puzzle" button numerous times.

Building the Ajax-Enabled Boggle Solver User Interface: Displaying the Puzzle's Solutions


At this point, we've seen how to collect the user's input and call the Solver.svc service's Solve method, but how do we display the results? As noted, when the server returns the solutions the JavaScript function DisplayResults will be invoked. In Using WCF Services with jQuery and the ASP.NET Ajax Library we displayed the results of the WCF service by looping through the results in JavaScript, concatenating each result to a string (along with formatting HTML) and then displaying that string in the page by assigning it to the inner HTML for an HTML element on the page.

That approach could certainly be used here, but a more sophisticated and maintainable approach is to use jQuery Templates. jQuery Templates is currently a jQuery plug-in, but will be a standard part of the jQuery library starting with jQuery version 1.5. (At the time of writing, the current version of jQuery is version 1.4.) jQuery Templates are a lot like ASP.NET's server-side templates that are used in the Repeater, ListView, FormView, and other controls. In a nutshell, they allow you to define your template as a mix of markup and special data-binding syntax. Then, from your JavaScript code, you can "bind" a collection of data to a template, which generates its HTML that can then be displayed on the page.

To use the jQuery Templates plug-in you'll need to reference the plug-in, either by downloading it directly to your web server or by referencing it through a CDN. The download available at the end of this article contains the plug-in in the Scripts folder (~/Scripts/jquery.tmpl.min.js). It can be accessed via CDN at http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js.

The display template is defined in the web page in the following <script> tag:

<script id="solutionsTemplate" type="text/html">
   <table class="solutions">
      {{each(i, solution) Solutions}}
         {{if i % 3 == 0}} <tr> {{/if}}
            <td class="Score${Score}">
               <a href="http://dictionary.reference.com/browse/${Word}">
                  ${Word} (${Score} {{if Score == 1}} pt) {{else}} pts) {{/if}}
               </a>
            </td>
         {{if i % 3 == 2}} </tr> {{/if}}
      {{/each}}
   </table>
</script>

Note that the template has an id of solutionsTemplate - this is how it will be referenced in JavaScript when it comes time to render the template.

Looking at the template you can probably surmise the HTML output it will generate. It starts by defining a <table> element. Next, it iterates through each of the solutions bound to the template. The syntax {{each(i, solution) Solutions}} means, "Enumerate over each object in the Solutions property. For each object let i be the current index (0, 1, 2, ...) and solution be assigned the object I'm currently enumerating over." Next, a conditional statement is used to emit a <tr> element only if i % 3 == 0. (i % 3 == 0 is true only when i is evenly divisible by 3, meaning it will be true when i is 0, 3, 6, 9, ... Likewise, the closing </tr> tag is emitted only when i % 3 == 2, or when i divided by 3 has a remainder of 2, such as when i is 2, 5, 8, ...)

Regardless of the value of i, a <td> element is emitted for each object in Solutions. The <td> contains a hyperlink that displays the solution and its score and links to the definition of the solution on Dictionary.com.

In addition to the template definition, the web page also includes a <div> element with an id of solutionsGrid, which is where the rendered template's output will be emitted: <div id="solutionsGrid"></div>.

The template is rendered by the DisplayResults function. (Recall that the DisplayResults function is executed when the HTTP request to Solver.svc?BoardID=boardId&Length=length is complete and the solutions have been returned to the browser.) The DisplayResults function is passed the JSON representation of the BoggleSolutionsDTO object returned from the WCF service. It starts by determining if there were any solutions and displays a short message indicating the number of solutions and total score.

Next, the template is rendered. This is accomplished by referencing the template via $("#solutionsTemplate") and then calling the tmpl function, passing in the BoggleSolutionsDTO object returned from the WCF service (results). The tmpl function returns the HTML the template generated, which is then appended to the solutionsGrid <div>.

After the template has been rendered and displayed, the buttons on the page are re-enabled (recall that they were disabled from the SolvePuzzle function). Also, the solutions are faded in, adding a little pizazz to the user experience.

function DisplayResults(results) {
   // Show the number of solutions and total score
   if (results.Solutions.length > 0)
      $("#solutionsGrid").html("<p>The following <b>" + results.Solutions.length + "</b> solutions exist for a total of <b>" + results.TotalScore + "</b> points:</p>");
   else
      $("#solutionsGrid").html("<p>There are no solutions for this board!</p>");

   // Render the template and display in solutionsGrid
   $("#solutionsTemplate").tmpl(results).appendTo("#solutionsGrid");

   // Re-enable the buttons
   $("input[type=button]").removeAttr('disabled');

   // Show the solutions
   $("#solutionsGrid").hide().fadeIn('slow');
}

For more information on jQuery Templates, see Reducing Code by Using jQuery Templates.

Comparing the "Cost" of the WebForms User Interface to the Ajax-Enabled User Interface


Using a tool like Fiddler you can examine the HTTP payloads exchanged between the client and server and determine the savings in bandwidth between the WebForms version and the Ajax-enabled version. Using Fiddler I measured both the request and response payloads for a particular Boggle board. Roughly, the WebForms interface submitted 2,500 bytes when clicking the "Solve Puzzle" button and received a payload back from the server clocking in at over 30,000 bytes. Clicking the "Solve Puzzle" button in the Ajax-enabled interface, on the other hand, sent just 400 bytes to the server and received just over 2,000 bytes back. In other words, the Ajax-enabled interface reduced the total amount of information exchanged by more than 30,000 bytes, or by 93%! Such a reduction in the information exchanged between the client and the server leads to a snappier user experience for all users.

The Ajax-enabled user interface offers a significant reduction in request and response bandwidth.

Happy Programming!

  • By Scott Mitchell


    Attachments:

  • Download the Demo Code Used in this Article

    Further Reading

  • Accessing Server-Side Data from Client Script: Using WCF Services with jQuery and the ASP.NET Ajax Library
  • Creating an Online Boggle Solver
  • View a Live Demo at www.FuzzyLogicInc.net/Boggle
  • jQuery Templates - Official Documentation
  • Reducing Code by Using jQuery Templates
  • Article Information
    Article Title: Updating My Online Boggle Solver Using jQuery Templates and WCF
    Article Author: Scott Mitchell
    Published Date: November 24, 2010
    Article URL: http://www.4GuysFromRolla.com/articles/112410-1.aspx


    Copyright 2017 QuinStreet Inc. All Rights Reserved.
    Legal Notices, Licensing, Permissions, Privacy Policy.
    Advertise | Newsletters | E-mail Offers