Exercise 2 - Displaying Upcoming Matches and League Tables

In this exercise you will import data into the Soccer website that contains information about the matches the team has played, upcoming matches that the team will play during the season, and the league tables that show the performance of each team in the league. You will then create a page that displays this data.

By the end of this exercise, you will understand how you can select and filter data in a database; how you can display the data as tables in a web page; how you can use HTML forms and controls to collect information from visitors, and how you can add some client-side interactivity to the page to make it easier for visitors to use. You'll also see some more ways that you can use CSS to specify layout and styling information for your web pages.

The Starting Solution

You can continue with the Soccer website you created in Exercise 1. Alternatively, you can use the solution we provide as your starting point. This solution, in the folder Examples\SoccerWebsite_02_Begin is a copy of the solution at the end of Exercise 1. If you decide to use the solution we provide as your starting point, carry out the following steps to copy it to your working solution folder.

To use the starting solution provided in the samples

  1. In Windows Explorer, navigate to the folder Examples\Exercise_02\SoccerWebsite_02_Begin.
  2. Select all of the files and subfolders and press CTRL-C to copy them.
  3. Navigate to your Documents\My Web Sites\SoccerWebsite folder (in Windows 7, this folder is in your Libraries folder).
    Hh145664.note(en-us,VS.99).gifNote:
    If you have not carried out the previous exercise to create the SoccerWebsite folder, right-click on the Documents\My Web Sites\ folder and click New, then click Folder. Change the name of the new folder to SoccerWebsite, and then open it in Windows Explorer.

  4. Press CTRL-V to paste all of the files into the Documents\My Web Sites\SoccerWebsite folder. If prompted, replace all of existing files with the new files.

This exercise comprises the following tasks:

  • Task 1 - Install and review the matches and league table data.
  • Task 2 - Prepare a page to display the match and league table data.
  • Task 3 - Add code to select and display a table of upcoming matches.
  • Task 4 - Add code to select and display the league table data.
  • Task 5 - Implement background loading for the league table.

You should be able to complete each of the tasks within 20-30 minutes.


Task 1 - Install and Review the Matches and League Table Data

In this task you will install a copy of the database we provide that includes a list of upcoming matches the team will play and the league tables into your solution. In subsequent tasks of this exercise you will use this data to generate tables that visitors can browse on the site.

To Install and review the matches and league table data

  1. Do not open the SoccerWebsite site in WebMatrix yet. First, you will copy the new database into your solution. In Windows Explorer, navigate to the samples folder named \Examples\Exercise_02\Resources\MatchesDatabase. Copy the file named StarterSite.sdf into the subfolder named App_Data of your Documents\My Web Sites\SoccerWebsite folder so that it replaces the existing file with the same name. In Windows 7, the Documents folder is within your Libraries folder.
    Hh145664.note(en-us,VS.99).gifNote:
    If the file copy dialog warns you that the file you are replacing is newer than the one that will replace it, ensure that you do in fact replace the existing file with the new one.

  2. Now start WebMatrix and open the SoccerWebsite site. You can either:
    1. Start WebMatrix, click the My Sites option in the start page, select the SoccerWebsite site in the list of recent sites, and click OK.
    2. Navigate to your Documents\My Web Sites folder in Windows Explorer, right-click on the SoccerWebsite folder, and then click Open as a Web Site with Microsoft WebMatrix.
  3. Click the Databases option in the lower-left panel of the WebMatrix main window. In the navigation tree view, expand the StarterSite.sdf item and then expand the Tables item to show the list of tables in the new database that you copied into your solution.

    Hh145664.65054fd6-17e9-4a73-bfa5-52b4a0b81799(en-us,VS.99).png

  4. Right-click the LeagueTable item and then click Definition to display the definition (the schema) of the table. You can see that it contains columns for all of the data required to display a table of information about the teams in the league. This includes the number of matches played, won, drawn, and lost and the current points tally for each team in the league. As the table can store details of the performance from several years, the table also contains a column that stores the year for each row. There is also a column that holds unique identifier (or key) that the database generates automatically for each row. This is called the identity column.

    Hh145664.3eec4f5d-6f25-4243-b644-bf96f8742d27(en-us,VS.99).png

  5. Click on the rows for each of the column definitions in the upper part of the schema editor and explore the properties for each one to ensure you understand the structure of the table.
  6. Now right-click the LeagueTable item in the tree view again, but—this time—click Data to show the data rows in the table. You can see that it contains the league table results for all of the teams. There are rows for the years 2010 and 2011. The table assumes that the 2011 season is not yet complete, and that the teams have more matches to play in this season.

    Hh145664.d695c916-c041-4e16-88c0-18709bbe98df(en-us,VS.99).png

  7. Click the small "x" in the tab at the top of the table data view to close it.
  8. Right-click the Matches item in the tree view and click Definition. This opens the table schema editor to show the definition of the Matches table. In addition to the column that holds the automatically generated unique key for each row, this table contains columns that store the date, opponents, result and a review of the match. It also has a bit field that stores a true or false value that indicates if this is an "away" match (as opposed to a match played at the home ground).

    Hh145664.82d7f95e-d9af-484e-a07f-77cdd3d26eae(en-us,VS.99).png

  9. As before, click on the rows for each of the column definitions in the upper part of the schema editor and explore the properties for each one to ensure you understand the structure of the table.
  10. Now right-click the Matches item in the tree view again and then click Data to show the data rows in the table. It contains rows for all of the teams you've just seen in the LeagueTable table. Notice that some of the rows contain a result and a review, and others do not. The schema of the table allows the MatchResult and MatchReview columns to contain NULL values for matches that have not yet been played. This means that the table can hold details of upcoming matches as well as matches that have already been played.

    Hh145664.b55ae89d-ff29-4f1b-b82d-5b4240a32da9(en-us,VS.99).png

    Hh145664.note(en-us,VS.99).gifNote:
    Because we had to fix the data at the time of writing, the dates of the un-played matches may be in the past when you come to use the examples. In this case you will have to imagine that you have taken a trip back in a time machine (not supplied with the examples) to the middle of 2011.

  11. Click the small "x" in the tab at the top of the data view to close it.

You have now completed this task. You have data available to your website code that you can use to display lists of matches and the league tables for two years. In the following tasks of this exercise you will create a page that displays this data.


Task 2 - Prepare a Page to Display the Match and League Table Data

In this task you will modify the default pages in the site that were generated by the template you used in Exercise 1 to display a page named Matches that will show the list of upcoming matches and the league tables. You will take advantage of the existing CSS styles defined in the default site style sheet that display the tabs to navigate to each page, and add additional styles to the style sheet for the new page layout and style.

Defining Page Layout with CSS Styles

The new page will display the information in two separate sections of the page, side by side as in columns of a newspaper or magazine. This makes good use of the available screen space because both of the tables of data are quite narrow.

Typically, before the advent of CSS, website developers used HTML tables to divide the page into separate sections so that the contents could be laid out as in a grid. Common layouts used a row at the top of the table for the site header, the left-hand table column to display the navigation links, the right-hand column to display the page content, and a bottom row to hold links and contact information.

However, you can achieve all of this using CSS to float sections of content to the left or right instead of being displayed as the default inline or block elements (inline elements follow each other on a line and wrap at the right hand end of the page, whereas block elements start on a new line in the page). The default template-generated site you used in the previous exercise uses CSS in this way to define the layout you saw there. This allows the complete appearance of the page, including both the style of elements and their positioning within the page, to be changed simply by using a different style sheet.

Hh145664.note(en-us,VS.99).gifNote:
For a great example of the way that the content, style, and overall appearance of a web site can be changed simply by changing the style sheet, see the CSS Zen Garden website at http://csszengarden.com/. Selecting a different style in the list causes a dramatic change to the page, with only the information (the text) staying the same.

In this exercise you will use CSS styles to divide the Matches page into two columns. For more information about using CSS styles to define page layout, see "Cascading Style Sheets, level 1 - Formatting Model" at http://www.w3.org/TR/2008/REC-CSS1-20080411/#formatting-model.

Using HTML Tables for Tabular Data

Having explained that you should use CSS to manage layout and positioning for sections of your web pages, the next question is "when should I use HTML tables?" The answer is that they are appropriate when the information you are displaying actually is tabular data. In other words, when the data consists of related sets of information that are notionally in rows and columns form. The sets of data in the LeagueTable and Matches tables in the database you added in the previous task are obvious examples of this type of information, and you will display this data using HTML tables.

Hh145664.note(en-us,VS.99).gifNote:
Another way to decide whether to use HTML tables or CSS styles is to think about whether visitors will expect to see the data as a table, where they read along rows or down columns to compare values. If this is the case, the information is in tabular form and should be displayed in an HTML table.

The core issue is the fact that tabular information is actually part of the data (content) of the page. It is not a feature of the display or UI layout information. By defining it as a table in the HTML content of the page, you provide context for the data, but still allow a style sheet to modify the appearance of the data by defining attributes such as the style, color, background, and more.

In addition, HTML tables provide some really useful features to support specialist user agents, such as browsers used by visually-impaired visitors. User agents like these can take advantage of extra information defined in the HTML elements of the table to make it much easier for such visitors to understand the contents and use the information. In later tasks of this exercise you will use HTML tables to display the data in the page.

For more information about defining HTML tables and the attributes the table elements support, see "HTML 4.01 Specification - Tables" at http://www.w3.org/TR/1999/REC-html401-19991224/struct/tables.html.

The Steps to Prepare the Matches Page

Now that you understand more about using CSS and HTML tables in your web pages, it's time to get started modifying the site you created in the previous exercise.

To prepare the Matches page to display league table and upcoming match data

  1. Continue with the SoccerWebsite site you modified in the previous task. If you have closed WebMatrix, open the site. You can either:
    1. Start WebMatrix, click the My Sites option in the opening screen, select the SoccerWebsite site in the list of recent sites, and click OK.
    2. Navigate to your Documents\My Web Sites folder in Windows Explorer (in Windows 7, this is within the Libraries folder), right-click on the SoccerWebsite folder, and then click Open as a Web Site with Microsoft WebMatrix.
    Hh145664.note(en-us,VS.99).gifNote:
    The default template you used in Exercise 1 added a page named About.cshtml to the site. As you already display information about the site in the Home page, you will change the About page to display the league tables and upcoming matches data.

  2. Click the Files option in the lower-left pane of the WebMatrix window to display a list of the files in the site.
  3. In the left-hand tree view, right-click the file About.cshtml and click Rename. Change the name to Matches.cshtml and press RETURN.
    Hh145664.note(en-us,VS.99).gifNote:
    You will see how to create a new page for your site, instead of modifying an existing one, in the following exercises.

  4. Double-click the file _SiteLayout.cshtml to open it in the main editor pane. This file defines the layout for all of the pages in the site, and contains the definition of the tabs that visitors use to navigate between pages.
  5. Locate the unordered list that defines the site navigation tabs ("Home" and "About"). Change the value of the href attribute and the text to display in the second tab, as shown in the following highlighted code.
      <ul id="menu">
        <li><a href="@Href("~/")">Home</a></li>
        <li><a href="@Href("~/Matches")">Matches</a></li>
      </ul>
    
    Hh145664.note(en-us,VS.99).gifNote:
    As you saw in Exercise 1, the Href method uses the ~ character to ensure that the URL it generates for the pages starts at the root folder of the site. The CSS style named menu and the associated menu styles defined in the Site.css style sheet create the tabbed appearance for the items in the list.

  6. Click the Save icon at the left of the WebMatrix window title bar and then click the small "x" in the main editor pane tab to close the _SiteLayout.cshtml file.
  7. In the tree view, double-click the Matches.cshtml file to open it in the code editor pane. This was the "About" page, and contains the code at the top to link to the _SiteLayout.cshtml master page and set the value of the Title property of the page. Other than this, it contains only a single paragraph of text. The following listing shows the complete content of this page at the moment.
    @{
        Layout = "~/_SiteLayout.cshtml";
        Page.Title = "About My Site";
    }
    <p>
        This web page was built using ASP.NET Web Pages. For more information...
    </p>
    
  8. Change the code that sets the Title property of the page as shown in the following highlighted code.
        Layout = "~/_SiteLayout.cshtml";
        Page.Title = "Upcoming Matches and League Tables";
    
  9. You want to display two columns of content in the page using CSS to manage the layout. Replace the entire <p> element and its content with the following definition of two <div> elements that will contain the list of upcoming matches and the league table data. At the moment, these will just contain placeholder information that you will replace with the real data in subsequent tasks of this exercise.
    <div class="floatLeftSection">
        <p>Upcoming matches table goes here</p>
    </div> 
    <div class="floatRightSection">
        <p>League table goes here</p>
    </div>
    
    Hh145664.note(en-us,VS.99).gifNote:
    Notice that each of the <div> elements carries a class attribute that specifies a set of CSS style selectors for that element. The <div> element is a block element, which means that—by default—it will be displayed as a section of content that fills the width of the browser window starting on the line below any content that precedes it. Any content after the closing </div> tag will start on the following line. To force the two <div> sections to be displayed side by side you will add styles named floatLeftSection and floatRightSection to the site's style sheet.

  10. Before you modify the style sheet, there is one addition required to the Matches page. You must ensure any content that might follow the two <div> sections starts on a new line, and is not wrapped by the <div> sections when you change them from the default block display to floating side-by-side sections. Add a third, empty <div> below them as shown in the following highlighted code to force any subsequent content to start on a new line below both of the floating sections.
    <div class="floatLeftSection">
        <p>Upcoming matches table goes here</p>
    </div> 
    <div class="floatRightSection">
        <p>League table goes here</p>
    </div>
    <div style="clear:both;" />
    
  11. Click the Save icon at the left of the WebMatrix window title bar and then click the small "x" in the main editor pane tab to close the Matches.cshtml file.
  12. In the tree view, expand the Styles folder and double-click the Site.css file to open it in the editor pane.
  13. Find the section with the comment "PRIMARY LAYOUT ELEMENTS" and insert the following section below it. This is where you will define the styles for the floated <div> elements.
    /* Floated section definitions
    ----------------------------------------------------------*/
    .floatLeftSection { 
    
    }
    
    .floatRightSection {
    
    }
    
    Hh145664.note(en-us,VS.99).gifNote:
    When you define the style for an element by using the class attribute on the element, you must precede the style definition in the style sheet with a period (.) to indicate that it is the definition of a CSS style class.

  14. To make an element float instead of being displayed as a block element, you use the float style selector and specify the position (left or right).
    /* Floated section definitions
    ----------------------------------------------------------*/
    .floatLeftSection { 
        float: left;
    }
    
    .floatRightSection {
        float: right;
    }
    
    Hh145664.note(en-us,VS.99).gifNote:
    By default, each <div> will "start" at the left or right of the page and expand to contain the content you place into in. To ensure that the second floated <div> lies to the right of the first one, and that the content wraps when the appropriate width is reached, you can specify the overall width of the floated sections. You can specify a fixed value in pixels, by using measurements based on the current font: points (pt) , picas (pc), em, or ex), or using length measures such as centimeters (cm), millimeters (mm), or inches (in). Alternatively, you can specify a percentage of the available parent (containing) element width.

  15. Add the following highlighted style selectors to the two style class definitions to specify that the two <div> sections should share the available width with a small space between them.
    /* Floated section definitions
    ----------------------------------------------------------*/
    .floatLeftSection { 
        float: left;
        width: 45%;
    }
    
    .floatRightSection {
        float: right;
        width: 45%;
    }
    
    Hh145664.note(en-us,VS.99).gifNote:
    In the example site the section of the final page that will contain the two floating <div> elements is the element <div id="content"> defined in the _SiteLayout.cshtml master page, which contains the call to the RenderBody method that forces the Matches page to be displayed within the content of the master page. One useful way to see the actual HTML that is sent to the browser is to run the page and then view the page source in the browser. In Internet Explorer, open the View menu and click Source.

  16. You want the content to be horizontally centered within the two sections, and slightly indented from the edges of the section of the page where they are displayed. Add the following highlighted selectors to the style class definitions to create a five pixel wide padding around each section, and to center the content horizontally within each section.
    /* Floated section definitions
    ----------------------------------------------------------*/
    .floatLeftSection { 
        float: left;
        width: 45%;
        padding: 5px; 
        text-align: center;
    }
    
    .floatRightSection {
        float: right;
        width: 45%;
        padding: 5px; 
        text-align: center;
    }
    
  17. Click the Save icon at the left of the WebMatrix window title bar and then click the small "x" in the main editor pane tab to close the Site.css file.
  18. In the tree view, select the file Default.cshtml and click the Run icon on the toolbar ribbon to display the Home page of the site in your browser. You will see the Matches tab where the About tab used to be.

    Hh145664.fc4a02d0-e8ed-40d0-83e8-53fe5cb771d8(en-us,VS.99).png


  19. Click the Matches tab to open the page you have created in this task. You will see the placeholder text in the two side-by-side sections of the page. These are the sections you created with the two floating <div> elements.

    Hh145664.4df5777e-7455-477e-bb30-eaf7b5fd5f2f(en-us,VS.99).png

You have now completed this task and you should have a better understanding of the ways that you can use CSS to define the layout of sections of content in a web page. You should also understand how the combination of an HTML unordered list and a set of style definitions makes it easy to add pages to the site and modify the existing navigation mechanism (the set of tabs about the main content section in the page).

Changing the Layout by Just Editing the Style Sheet

As an additional task that will help you to see how easily you can change the layout of a page using a separate CSS style sheet, open the Site.css file and swap the names of the two style classes you added in this task, as shown in the following highlighted code.

.floatLeftSection { 
    float: right;
    width: 45%;
    padding: 5px; 
    text-align: center;
}

.floatRightSection {
    float: left;
    width: 45%;
    padding: 5px; 
    text-align: center;
}

Save the Site.css file and then open the Matches page in your browser. You will see that the two floating content sections are reversed (as shown in the following screenshot). Remember to change the styles in the Site.css file back to their previous values after you try this or your page will not match that shown in the following tasks of this exercise!

Hh145664.21052efb-1733-4756-b1a5-ece7c2df255b(en-us,VS.99).png


Task 3 - Add Code to Select and Display a Table of Upcoming Matches

In this task you will add code to the Matches page that displays a list of upcoming matches. You will insert this information into the page using an HTML table, and include information that makes it easier for users of specialist user agents (such as web browsers and tools designed for use by visitors with visual impairments) to understand the data.

Rendering Data for Non-Visual User Agents

When a specialist user agent such as a page reader used by visually impaired visitors loads a web page, it can make use of information in the page that is not visible in normal web browsers. This information can include additional descriptions of the elements and content, such as the captions for images that you added using an alt attribute on the news item images in Exercise 1.

Tables of information are much harder for users of non-visual user agents to comprehend because they cannot simply scan by eye across the rows and down the columns. Instead, the user agent will read aloud the values in each cell of the table, and by adding specific attributes to the elements that make up the table you can provide additional information about the contents that helps the user to mentally visualize the context of each value.

For example, you can add a caption and summary description of the contents so that the user can decide whether to investigate the contents or just skip over the table. You can also add attributes to column headers and row headers to further describe that row or column, and to individual cells to more clearly indicate the column and row header to which they apply. For example, in the following schematic, you can see that each column and row header specifies the scope to which it applies, and carries a unique value for its id attribute. Each of the cells within the rows specifies the column and row header that apply to it.

Hh145664.31920c8a-e68e-40f0-a191-b92e4512a633(en-us,VS.99).png

This makes it easy for the user agent to speak the values in context when the user navigates through the rows and columns. For example, it may read the value of the cell in the second column of the first row as "10 July, Team, Litware United". In a small and symmetrical table such as this, the additional information these attributes provide may not be vital, and many specialist user agents will cope well with these kinds of tables without the additional information. However, when you start to use more complex tables, particularly those that contain merged (spanned) cells, column groups, subheadings, and sub totals, you should endeavor to provide as much context as possible using these techniques.

For more information about creating tables that work well with non-visual and specialist user agents, see "Table rendering by non-visual user agents" in the page "HTML 4.01 Specification - Tables" at http://www.w3.org/TR/1999/REC-html401-19991224/struct/tables.html#non-visual-rendering.

Hh145664.note(en-us,VS.99).gifNote:
WebMatrix provides a helper class you can use to create HTML tables that include the capability to sort the rows and view the contents as individual pages. You will see how this class is used in Exercise 5 where you create administration pages for the site.

The Steps for Displaying the Table of Upcoming Matches

Now that you understand more about the requirements for non-visual user agents and the way that HTML tables can assist users of these and other specialist tools, you can start to add the code to your Matches page to select the required rows from the database and display this data in the page.

To select and display the upcoming matches data in an HTML table

  1. Continue with the SoccerWebsite site you modified in the previous task.
  2. Click the Files link in the lower-left pane of the WebMatrix window to display the files in the site, and then double-click the file Matches.cshtml to open it in the code editor pane.
  3. First, you will add the code to extract the required data from the database. Add the following highlighted code to the start of the Matches.cshtml page to open the database and execute a SQL query that selects only the rows in the Matches table where there is no value in the MatchResult column, and sorts them into date order.
    @{
      Layout = "~/_SiteLayout.cshtml";
      Page.Title = "Upcoming Matches and League Tables";
        
      var db = Database.Open("StarterSite");
        
      // Get a list of matches that have not yet been played. To avoid issues 
      // with the dates in this example, we're choosing rows that do not contain 
      // a result value for the match.
      var upcomingMatches = db.Query("SELECT * FROM Matches " 
          + "WHERE (MatchResult IS NULL OR MatchResult = '') ORDER BY MatchDate");
    }
    
    Hh145664.note(en-us,VS.99).gifNote:
    It would seem more sensible to select matches where the match date is in the future by comparing the match date to the current date obtained using the T-SQL GETDATE function. However, this will not work if you are running the sample we provide after the fixed dates of the matches in the sample database, so we chose instead to select matches that have no result. In your own sites, you must, of course, design your queries to ensure that they return the correct sets of rows.

  4. Now you can start to add the code to create the HTML table. It will go into the left-hand floating <div> section that you added to the page in the previous task. Replace the <p> element you added to that section in the previous task with the following highlighted HTML that creates the outline of the table, including the summary of the contents for non-visual user agents and the caption to be displayed above the table.
    <div class="floatLeftSection">
      <table summary="Table that lists the date of upcoming matches,
                      the opposing team name, and whether they are 
                      Home or Away matches.">
        <caption>Upcoming Matches</caption>
        <thead>
    
        </thead>
        <tbody>
    
        </tbody>
      </table>
    </div>
    
    Hh145664.note(en-us,VS.99).gifNote:
    Using the <thead> and <tbody> elements, and <tfoot> if you have a footer row such as a row of totals in the table, is recommended practice; even though the table will display fine without them. However, including them makes it easier for automated page readers to understand the context. In addition, for very long tables, some browsers may be able to use the attributes to scroll the data rows while keeping the header and footer rows visible.

  5. Next, you can add the HTML to create the column header row for the table. Add the following highlighted HTML to the <thead> section of the table definition. Notice that each header cell (<th> element) in the table row (<tr> element) includes the scope attributes to specify that the headers apply to their respective columns. The two columns that will contain the names of the opposing team and the location also include the id attribute with an appropriate value.
        <thead>
          <tr>
            <th scope="col">Date</th>
            <th id="mc1" scope="col">Opponents</th>
            <th id="mc2" scope="col">Location</th>
          </tr>
        </thead>
    
    Hh145664.note(en-us,VS.99).gifNote:
    Creating the HTML output for the <tbody> section rows requires a little more effort that than required for the header row (shown above). In particular, the section of non-header rows requires some code to iterate through the rows in the data set retrieved from the Matches table in the database. In addition, the code must generate unique values for the id attribute for each row header, and the appropriate values for the headers attributes of each data cell.

  6. Start creating the <tbody> section by adding the following highlighted code that initializes a variable that will act as a row counter, and then iterates through all the rows in the data set (in the same way as you saw for the news items in Exercise 1).
        <tbody>
        @{
          int matchRowNumber = 1;
          foreach(var matchItem in upcomingMatches)
          {
    
          }
        }
        </tbody>
    
    Hh145664.note(en-us,VS.99).gifNote:
    The variable upcomingMatches was created and populated with a collection of data rows at the start of the page. Each row is temporarily referenced by the variable matchItem within the foreach construct.

  7. Inside the foreach loop, your code must create the string values required for the element attributes, and any other values you require. Add the following highlighted code to the foreach construct to create a string value for the row header in the form "mrx" where x is the row counter value, and string values for the attributes of the cells in each column in the form "mcx,mry" where x is the column header id number and y is the row header id number. At the end of the foreach loop, the code increments the loop counter variable.
          foreach(var matchItem in upcomingMatches)
          {
            string rowId = "mr" + matchRowNumber.ToString();
            string headersId1 = "mc1,mr" + matchRowNumber.ToString();
            string headersId2 = "mc2,mr" + matchRowNumber.ToString();
            string homeOrAway = matchItem.MatchIsAway ? "Away" : "Home";
    
            matchRowNumber += 1;
          }
    
    Hh145664.note(en-us,VS.99).gifNote:
    Notice that the code above also creates a string named homeOrAway based on the value in the MatchIsAway column of the table, which contains Boolean (true or false) values. This means that the table can display either "Home" or "Away". This type of code expression is called a ternary operator.

  8. The final step is to add the HTML to generate the data rows for the table. The first column in each row is a header for the row (it displays the date, which in this case is the important value that defines the context for the row), and the remainder are standard detail cells (<td> elements). All of the values required for the attributes and the cell values are available from the string variables just created and the current data row referenced by the matchItem variable.
          foreach(var matchItem in upcomingMatches)
          {
            string rowId = "mr" + matchRowNumber.ToString();
            string headersId1 = "mc1,mr" + matchRowNumber.ToString();
            string headersId2 = "mc2,mr" + matchRowNumber.ToString();
            string homeOrAway = matchItem.MatchIsAway ? "Away" : "Home";
            <tr>
              <th id="@rowId" 
                  scope="row">@matchItem.MatchDate.ToString("d MMM yyyy")</th>
              <td headers="@headersId1">@matchItem.MatchOpponents</td>
              <td headers="@headersId2">@homeOrAway</td>
            </tr>
            matchRowNumber = matchRowNumber + 1;
          }
    
  9. Click the Save icon at the left of the WebMatrix window title bar. Then ensure that the Matches.cshtml file is selected in the left-hand tree view pane, and click the Run icon on the toolbar ribbon to open the page in your browser. You will see the new table displaying the list of upcoming matches.

    Hh145664.351661e8-232b-4499-9780-3b7464ca284b(en-us,VS.99).png

  10. Now use the commands in your browser to view the source of the page. For example, in Internet Explorer open the View menu and click Source. Scroll down to find the output generated for the HTML table, and examine the result. We've listed an abridged section of it here. You can see the values of the id attributes in the <th> header cells of each row, and the headers attribute values that map each <td> cell to the corresponding column and row headers.
    <table summary="Table that lists the date of upcoming matches,
                    the opposing team name, and whether they are 
                    Home or Away matches.">
      <caption>Upcoming Matches</caption>
      <thead>
      <tr>
          <th scope="col">Date</th>
          <th id="mc1" scope="col">Opponents</th>
          <th id="mc2" scope="col">Location</th>
      </tr>
      </thead>
      <tbody>
          <tr>
              <th id="mr1" 
                  scope="row">1 Jul 2011</th>
              <td headers="mc1,mr1">Woodgrove Bank Rovers</td>
              <td headers="mc2,mr1">Away</td>
          </tr>
          <tr>
              <th id="mr2" 
                  scope="row">8 Jul 2011</th>
              <td headers="mc1,mr2">Tailspin Athletic</td>
              <td headers="mc2,mr2">Home</td>
          </tr>
        ...
      </tbody>
    </table> 
    
    Hh145664.note(en-us,VS.99).gifNote:
    OK, so you have generated and displayed the table of data just as required but, let's be honest, it doesn’t really look very attractive. In the remainder of this task, you will tidy it up using CSS styles.

  11. Close your browser and go back to the Matches.cshtml file (which should still be open in the WebMatrix editor pane) and find the opening <table> tag. Add a class attribute to it as shown in the following highlighted code so that you can define a set of CSS style selectors that apply to the entire table and to its child elements.
    <div class="floatLeftSection">
      <table class="htmlTableList"
             summary="Table that lists the date of upcoming matches,
                      the opposing team name, and whether they are 
                      Home or Away matches.">
    
  12. Now find the <td> element that displays the team name. This would look a lot better with the text aligned left instead of centered. Add a class attribute to it as shown in the following highlighted code.
        <tr>
          <th id="@rowId" 
              scope="row">@matchItem.MatchDate.ToString("d MMM yyyy")</th>
          <td headers="@headersId1"
              class="tableCellAlignLeft">@matchItem.MatchOpponents</td>
          <td headers="@headersId2">@homeOrAway</td>
        </tr>
    
    Hh145664.note(en-us,VS.99).gifNote:
    Notice that the names used for the CSS classes are relatively generic, in that they are not specific to the table of upcoming matches. This is so that they can be used with other HTML tables in other parts of the site where you want the same style and overall appearance.

  13. Click the Save icon at the left of the WebMatrix window title bar. Then expand the Styles folder and double-click the style sheet file Site.css to open it in the WebMatrix editor pane.
  14. Scroll down to the end of the section with the comment "Floated section definitions" that you added in the previous task of this exercise, and insert the following style definitions section immediately below it.
    /* HTML tables, headings, and row cells
    ----------------------------------------------------------*/
    .htmlTableList {
        border-collapse: collapse;
    }
    
    Hh145664.note(en-us,VS.99).gifNote:
    This style for the HTML <table> element causes the table to be displayed without padding or gaps between the cells. It generally gives a neater appearance when you define background colors and borders for cells in the table.

  15. You can now easily define styles that apply to the child elements within the HTML <table> element by specifying the element name after the class name. The styles will only apply to elements of that type which are children or descendants of an element that carries a class attribute for the specified style class. Add the following style definition that will apply to the <caption> child element to specify a large bold font, and to introduce some padding between it and the heading text above the table.
    .htmlTableList caption {
        font-weight: bold;
        padding: 15px 10px;
        font-size: 18px;
    }
    
    Hh145664.note(en-us,VS.99).gifNote:
    Next, you will define the styles for the table header and detail cells by specifying child or descendent element types <th> and <td>. By default, browsers display <th> cell content in a bold font, though it does no harm to include this in the style definition as well. In addition, you will change the background color, text color, border color, and cell padding.

  16. Add the following two style class definitions to the Site.css style sheet. These specify the headers as white text on a dark green background, while the detail cells will have an off-white background (the same color as the navigation tabs in the page). The padding values are designed to make the table easy to assimilate without making it too wide. Recall that you will eventually place two tables side by side in this page.
    .htmlTableList th {
        font-weight: bold;  
        padding: 4px 5px;
        border: 2px solid #669900;  
        background: #669900;  
        color: #FFFFFF;  
    }
    
    .htmlTableList td {
        padding: 2px;  
        border: 2px solid #669900;  
        background: #e8eef4;  
        padding: 4px 5px;
    }
    
  17. Finally, add the following style class for the table cells that you want to be left-aligned.
    .tableCellAlignLeft {
       text-align:left;
    }
    
  18. Click the Save icon at the left of the WebMatrix window title bar. Then ensure that the Matches.cshtml file is selected in the left-hand tree view pane, and click the Run icon on the toolbar ribbon to open the page in your browser. You will see the updated table displayed using the new styles you just defined.

    Hh145664.e2954eea-dd6c-46f7-a2b5-4f26166790cf(en-us,VS.99).png

You have now completed this task and you should have a firm understanding of how you can create HTML tables using code, including the relevant and useful additional information for non-visual user agents and automated page readers. You should also understand more about the ways that you can apply styles to change the appearance of a page and its content using CSS style sheets. In the next task, you will add another table to the Matches page, this time to display the league table data.


Task 4 - Add Code to Select and Display the League Table Data

In this task you will add another table to the Matches page, this time to display the league table data. Fundamentally, this is a similar process to that you used to generate the table of upcoming matches. However, the database contains league table data for more than one season, so there are additional issues to contend with as well as an opportunity to make the page interactive.

The first issue you must resolve is how to select the appropriate data to display by default—when a visitor first opens the page. After that you must consider how you will allow the user to select a different season and then display the data for that season instead.

HTML Forms and Postbacks

HTML forms provide a mechanism for a user to submit values and data to a web server. Traditionally, the site would provide an HTML page containing HTML controls such as text boxes, option button, check boxes, and buttons. A visitor enters values into the controls on this page and then clicks a "submit" button to send the values as a request to a special page on the web server that can handle these values and perform the required actions with the data.

However, with the advent of dynamic page generation techniques such as ASP.NET, PHP, and others an approach called postback has become a popular approach. The page contains the controls the visitor interacts with, within an HTML <form> section, but the request is sent (posted) back to the same page. The page then extracts the data from the request, performs any required actions with the data, and then regenerates the page with the updated content.

More advanced web page generation mechanisms tend to hide this process behind clever UI controls and JavaScript functions in libraries (such as the jQuery library you used in the previous exercise) that are loaded with the page. However, for simple tasks such as selecting a year, as in this example, you can easily write code that handles the postback and changes the content of the page to show updated information. You can collect the values sent from the controls on the form in your code by querying properties of the built-in Request object, and then use these values to modify your database queries and the HTML that the page generates.

For more information about creating and using HTML forms, see "HTML 4.01 Specification - Forms" on the W3C site at http://www.w3.org/TR/html4/interact/forms.html and "Tutorial - Working with Forms" on the ASP.NET WebMatrix site at http://www.asp.net/webmatrix/tutorials/4-working-with-forms.

The Steps for Displaying the League Table Data

Now that you understand more about using HTML forms in your web pages, you can get started modifying the site you created in the previous exercise to display the league table data.

To display a table containing the league table data

  1. Continue with the SoccerWebsite site you modified in the previous task.
  2. Click the Files option in the lower-left pane of the WebMatrix window and double-click the file named Matches.cshtml to open it in the code editor pane.
    Hh145664.note(en-us,VS.99).gifNote:
    The LeagueTable table in the database contains rows from more than one season, with each season identified by a column named LeagueYear that contains the year number. The first part of this task is to extract the required data rows from the league table. To decide which season's rows to extract from the database, you must check if the visitor selected a season in the page and then submitted it or whether this is the first time that the visitor has opened this page—and they have not, therefore, selected a season. In the latter case, you will display the league table for the current season. However, because the database we provide only contains rows for 2010 and 2011 seasons, the code in the example will default to displaying the 2011 season if the visitor did not select a season.

  3. Add the following highlighted code to the code section at the top of the page. This sets the value for the season year by querying the collection of values exposed by the QueryString property of the Request object to get the value that was selected in the control named leagueTableSelector. It then converts this into an integer year number, or uses the default value (in this case 2011) if there was no value in the QueryString collection.
    @{
      ...
      var upcomingMatches = db.Query("SELECT * FROM Matches " 
          + "WHERE (MatchResult IS NULL OR MatchResult = '') ORDER BY MatchDate");
        
      // Define a variable for the league table year to display by default. 
      // Use the value posted from the form if available, or the default if not.
      // NB: In the real world application, you would use the following to generate 
      // the value for the current year instead of using the fixed value 2011:
      // int leagueDisplayYear 
      //     = Request["leagueTableSelector"].AsInt(DateTime.Today.Year;
      int leagueDisplayYear = Request["leagueTableSelector"].AsInt(2011);
    
    }
    
    Hh145664.note(en-us,VS.99).gifNote:
    If this is the first time the visitor has loaded the page, the QueryString collection will not contain a value from the control, and so the variable leagueDisplayYear will contain the default value 2011. In your own "real world" applications, you would instead use the code in the comment above to set it to the current year. However, if there is a value in the QueryString collection, the variable leagueDisplayYear will contain the year number selected by the visitor.

  4. Now that you have the appropriate year number, you can create a SQL query that selects only the rows from the required season and execute it to create the collection of rows that you will display in the table. Add the following highlighted code to the end of the code section at the top of the Matches.cshtml file.
    @{
      ...
      int leagueDisplayYear = Request["leagueTableSelector"].AsInt(2011);
    
      // Get a list of league table rows for the selected year sorted in descending 
      // order by points, and then by the number of matches won/drawn/played.
      // Note the use of the replaceable parameter @0 in the SQL statement.
      var leagueTable = db.Query("SELECT * FROM LeagueTable WHERE LeagueYear = @0 " 
        + "ORDER BY LeagueTotalPoints DESC, LeagueMatchesWon DESC, "
        + "LeagueMatchesDrawn DESC, LeagueMatchesPlayed DESC", leagueDisplayYear);
    
    }
    
    Hh145664.note(en-us,VS.99).gifNote:
    One important point to note here is the use of a parameterized SQL statement. If you build SQL statements by concatenating SQL commands with values submitted by visitors, you leave your code open to a type of attack called SQL Injection. Malicious visitors may submit a value that is interpreted by the database as one or more SQL commands instead of a value, and this can introduce invalid data, corrupt existing data, allow unauthorized access to the site, or even delete the database altogether. Whenever you handle input in SQL statements, especially text input, you should consider using replaceable parameters (as shown in the code above). Values entered by visitors will simply be passed to the database as parameter values, and will not be interpreted as commands. If a parameter value is invalid, the database will reject it and will not execute the statement.
    You may think the fact that the value comes from a drop-down list where visitors can only select an existing value protects against this type of attack. However, as you have just seen, the value arrives in the query string of the request, and it would be easy for a malicious visitor to send a request with any text appended in the query string. It is also easy to send requests with custom modified values in the Form collection, so this is no defense either.
    One mitigating factor in the code used in the example above is that it is converted to an integer before being used in the query, which will prevent commands from being included in the value sent to the database. However, you or another developer may change the code in the future without realizing the impact of removing a data type conversion or other code such as this. For all of these reasons, you should always consider using replaceable parameters in your SQL statements.

  5. There is one more task you must accomplish before you finish collecting the data for the page. You want to be able to display a list of available seasons (year numbers) from which the visitor can select. You could use a hard-coded list, but then you would need to maintain this as you added or removed rows in the LeagueTable table. A better solution is to create a list of the years for which data is available by querying the database. Add the following highlighted code to the end of the code section in the Matches.cshtml page. This uses a SQL query that contains the DISTINCT keyword so that it will return only unique (different) values from the set of matching rows. The result is a row set containing a single column of unique year numbers. The query also specifies that this column should be named YearNumber.
    @{
      ...
      + "LeagueMatchesDrawn DESC, LeagueMatchesPlayed DESC", leagueDisplayYear);
        
      // Get a list of just the unique year numbers in the LeagueTable table rows.
      // This list is used to create the drop-down year selector for the table.
      var leagueYears = db.Query("SELECT DISTINCT LeagueYear As YearNumber "
          + "FROM LeagueTable ORDER BY LeagueYear");
    }
    
  6. Now you can start adding the definition of the table to the HTML in the page. Find the <div> element that you added to the page in the previous exercise to contain the league table, and replace the default <p> element and the text within it with the following highlighted HTML that generates the table.
    <div class="floatRightSection">
      <table class="htmlTableList"
          summary="Table that shows the cumulative results for each team
                      in the league sorted by the total number of points.
                      Select a different year to see the league table for
                      previous years.">
        <caption>League Table</caption>
        <thead>
          <tr>
              <th scope="col" abbr="Position"></th>
              <th id="lc1" scope="col">Team</th>
              <th id="lc2" scope="col" abbr="Played">P</th>
              <th id="lc3" scope="col" abbr="Won">W</th>
              <th id="lc4" scope="col" abbr="Drawn">D</th>
              <th id="lc5" scope="col" abbr="Lost">L</th>
              <th id="lc6" scope="col">Points</th>
          </tr>
        </thead>
        <tbody>
        @{
          int leaguePosition = 1;
          foreach(var leagueItem in leagueTable)
          {
            string rowId = "lr" + leaguePosition.ToString();
            string headersId1 = "lc1,lr" + leaguePosition.ToString();
            string headersId2 = "lc2,lr" + leaguePosition.ToString();
            string headersId3 = "lc3,lr" + leaguePosition.ToString();
            string headersId4 = "lc4,lr" + leaguePosition.ToString();
            string headersId5 = "lc5,lr" + leaguePosition.ToString();
            string headersId6 = "lc6,lr" + leaguePosition.ToString();
            <tr>
              <th id="@rowId" scope="row">@leaguePosition</th>
              <td headers="@headersId1" 
                  class="tableCellAlignLeft">@leagueItem.LeagueTeamName</td>
              <td headers="@headersId2">@leagueItem.LeagueMatchesPlayed</td>
              <td headers="@headersId3">@leagueItem.LeagueMatchesWon</td>
              <td headers="@headersId4">@leagueItem.LeagueMatchesDrawn</td>
              <td headers="@headersId5">@leagueItem.LeagueMatchesLost</td>
              <td headers="@headersId6">@leagueItem.LeagueTotalPoints</td>
            </tr>
            leaguePosition = leaguePosition + 1;
          }
        }
        </tbody>
      </table>
    </div> 
    
    Hh145664.note(en-us,VS.99).gifNote:
    This HTML and code is very similar to that used in the previous task to generate the "Upcoming Matches" table. It generates the appropriate table structure and adds the attributes that assist users of non-visual user agents and specialist browsers. Look back as Task 3 of this exercise for more details. Also notice how you can use the abbr attribute in a header cell to instruct non-visual user agents to read a different description of the column. The code above changes the header text for non-visual user agents to something more descriptive that the single letters used in the visible table, and uses "Position" for the first column where there is no visible header text.

  7. Click the Save icon at the left of the WebMatrix window title bar and then click the Run icon. You will see the new table appear alongside the "Upcoming Matches" table.

    Hh145664.20705967-ae02-484f-9a75-b6d78c34757f(en-us,VS.99).png

    Hh145664.note(en-us,VS.99).gifNote:
    This displays the current season's data because you haven’t initiated a postback to select a different season. But how do you allow visitors to select the season they want to see? To resolve this, you will now add controls to the page that display a list of available seasons and allow the visitor to select the one required.

  8. Close your browser and go back to the Matches.cshtml page in WebMatrix. You need to define an HTML form section on the page to contain the controls the visitor will interact with. Add the following highlighted code lines to create a <form> element around the existing html and code in the page.
    <form id="leagueTableForm" action="" method="get">
      <div class="floatRightSection">
        <table class="htmlTableList"
        ...
        ... existing HTML definition of the table is here
        ...
        </table>
      </div> 
    </form>
    
    <div style="clear:both;" />
    
    Hh145664.note(en-us,VS.99).gifNote:
    Notice that the code sets the value of the method attribute of the form to "get". The method attribute specifies if the values will be sent as name/value pairs appended to the end of the URL as a query string (method="get") or as values embedded within the headers of the request (method="post"). Using method="post" and querying the Form collection of the Request object helps to hide the values and makes the URL less verbose. However, using method="get" and querying the QueryString collection allows you to specify the page URL and the values it requires in a shortcut, or in a link in a document or email. For example, you would be able to display the values for the 2010 season directly by opening a link such as the following when the site is hosted on a commercial web server:
    http://MySoccerWebsite/Matches.cshtml?leagueTableSelector=2010.
    Note that you can also query the Request object directly, instead of specifying a collection, to get a value irrespective of whether it is in the Form or the QueryString collection (or, in fact, any other collection exposed by the Request object). For example, the following line of code returns the value of a control named leagueTableSelector from either the Form or the QueryString collection:
    var controlValue = Request ["leagueTableSelector"];

  9. Now find the <caption> element in the definition of the league table and replace the existing text "League Table" with the following highlighted HTML and code. This inserts a drop-down list (an HTML <select> element) that displays a list of the available season years, with the currently chosen year selected. Notice that the code sets both the id and the name attributes of the <select> element to the same value. This is necessary to retrieve the control value in the postback and also be able to access the control in client-side code.
      <caption>League Table for
        <select id="leagueTableSelector" name="leagueTableSelector">
        @foreach (var yearItem in leagueYears)
        {       
          <option @if(yearItem.YearNumber == leagueDisplayYear)
                  {<text>selected="selected"</text>}>@yearItem.YearNumber</option>
        }
        </select>
      </caption>
    
    Hh145664.note(en-us,VS.99).gifNote:
    The code above iterates through the collection of year numbers (referenced by the variable named leagueYears) that you created in the code at the top of this page. It populates the <select> element with a series of <option> elements, one for each year. As it does so, it compares the currently selected year (stored in the variable named leagueDisplayYear) with the value in the <option> element it is generating. If they are the same, it adds the attribute selected="selected" to the <option> element.

  10. Save your Matches.cshtml file and click the Run icon to display the Matches page. You will see the drop-down list above the table. You can open it to see the two available season year numbers.

    Hh145664.d66f988a-988f-45a8-8d19-3a9711f8bbf7(en-us,VS.99).png

    Hh145664.note(en-us,VS.99).gifNote:
    If you view the source of the page in your web browser, you will see the following HTML (it will contain additional line breaks, but these do not affect the way it works):
    <select id="leagueTableSelector" name="leagueTableSelector">
    <option >2010</option>
    <option selected="selected">2011</option>
    </select>

  11. At the moment the drop-down list does not look very attractive. It doesn’t match the surrounding text and style. In the tree view of files in WebMatrix, expand the Styles item and double-click the file Site.css to open it in the editor.
  12. Find the section starting with the comment "HTML tables, headings, and row cells" that you added in an earlier task of this exercise, and add the following CSS style class definition to it.
    .tableSelectList {
        color: #696969;
        font-weight: bold;
        xpadding: 15px 10px;
        font-size: 18px;
        border: none;
    }
    
  13. Save and close the Site.css file and go back to the Matches.cshtml file. Locate the <select> element and add a class attribute for the new style class as shown in the following highlighted code.
        <select id="leagueTableSelector" name="leagueTableSelector"
                class="tableSelectList">
    
  14. Save the Matches.cshtml file, then click Run to open it in your browser. You can see that it look a lot nicer, and matches the style of the text. Typically you should consider styling the HTML controls you use in your sites as the default display styles do not usually match your site or look very attractive.

    Hh145664.9ccd274a-0d28-4724-aada-e27998ff2190(en-us,VS.99).png

    Hh145664.note(en-us,VS.99).gifNote:
    However, there is another problem. You may have noticed that selecting a year does nothing. It doesn’t post the page back to the server or update the values in the table. You must add either code or another HTML control to cause the page to be posted to the server. Most web sites these days use client-side JavaScript or jQuery code to initiate a postback when a visitor interacts with a control on the page. Where there is more than one control on the page, the typical approach is to provide a button or image that the visitor clicks to submit the control values. You will add some client-side code to initiate a postback in your Matches page when a visitor selects a value in the drop-down list of year numbers.

  15. In the Matches.cshtml file, find the <select> element above the league table and add an onchange attribute to it that executes an inline JavaScript statement, as shown in the following highlighted code.
        <select id="leagueTableSelector" name="leagueTableSelector"
                class="tableSelectList"
                onchange='document.forms["leagueTableForm"].submit();'>
    
    Hh145664.note(en-us,VS.99).gifNote:
    The JavaScript code shows a simple inline scripting approach. The code executes when a visitor changes the selection in the drop-down list. It calls the submit method of the HTML form named leagueTableForm that encloses the controls and the league table to send a request for the same page (including the values selected in the controls within the form) to the server. Notice how the attribute is enclosed in single quotes because the JavaScript statement contains double quotes.

  16. Save the Matches.cshtml file, then click Run to open it in your browser. This time, when you select a different year in the drop-down list, the page reloads and shows the appropriate set of values in the league table.
  17. However, there is another issue to resolve. Use the options in your web browser to disable script execution:
    1. In Internet Explorer, open the Tools menu and click Internet Options. Open the Security tab and click the Custom Level button. Scroll down though the options that are shown to find the Scripting section, and change the Active Scripting option to Disable. Then click OK until you get back to the main browser window.
    2. In Firefox, open the Tools menu and click Options. In the Options dialog click the Content icon and uncheck the Enable JavaScript option. Then click OK.
  18. Now refresh the Matches page in your browser and try to select a different year. It obviously won't work because the JavaScript code will not execute. The jQuery approach, which also depends on JavaScript, will not work either. Some non-visual user agents and specialist browsers do not support JavaScript, or an over-cautious visitor may have disabled it as a security precaution. Thankfully, there is a very simple solution. Add the following highlighted code that defines an HTML submit button within a <noscript> element to your Matches page, placing it after the <select> element and before the closing </caption> tag.
        ....
        </select>
        <noscript><input type="submit" value="Go" /></noscript>
      </caption>
    
  19. Save the Matches.cshtml file and refresh the page in your browser. You will see a button next to the drop-down list. Select the year you want and click the button. The page reloads with the updated values in the table.

    Hh145664.97406562-e2d5-4bda-b7de-2a72bc0dc8ab(en-us,VS.99).png

    Hh145664.note(en-us,VS.99).gifNote:
    You can, of course, add a style definition for the button—but as it is there primarily for non-visual user agents there seems little point in doing so.

  20. Now use the options in your browser to re-enable script execution, and refresh the Matches page. You will see that the "Go" submit button no longer appears. The HTML <noscript> element allows you to include content in a page that is only displayed when the browser or user agent that loads it does not support client-side script execution (or it is disabled in the browser options).
  21. To end this task, select a year in your Matches page and then look at the address bar of your browser. You will see that the URL. It includes the query string that specifies the selected year. Typically this will be something like the following, though the port number after the "localhost:" part will probably be different on your computer.
    http://localhost:29843/Matches.cshtml?leagueTableSelector=2010
    

You have now completed this task and you should have started to explore how you can use HTML forms, HTML controls, and code to make a page interactive and to change the content of the page based on values sent to it from the browser. In the next and final task of this exercise you will see how you can improve the usability and efficiency of the Matches page by loading updated values asynchronously in the background.



Task 5 - Implement Background Loading for the League Table

In this task you will modify the Matches page so that it loads and displays the league table asynchronously in the background, instead of reloading the entire page each time a visitor selects a different season.

Hh145664.note(en-us,VS.99).gifNote:
Asynchronous execution occurs separately from the main code execution (technically, it runs on a different operating system execution thread), and so it can execute at the same time as other code that is running in the foreground. This means that the page does not "freeze up" or become unresponsive while the background task is executing.

At the moment, when a visitor changes the selection in the drop-down list of season year numbers, the code in the page causes a postback that regenerates and sends to the browser the entire page. However, this is inefficient because the only part that is different is the content of the league table. The table of upcoming matches is the same, as is the remainder of the page (the header, footer, and background).

It is common for websites these days to implement a partial page refresh mechanism where only the changed parts of the page are reloaded (or refreshed). This reduces bandwidth usage, and makes the operation of the site seem smoother because visitors do not see the full page reload. There are several techniques available for partial page refresh, including the use of technologies such as Microsoft Silverlight, Flash animations, or JavaScript/jQuery code that updates elements in the page.

In this task, you will use jQuery, but in a way that makes use of a separate page that contains only the league table. You will reload this page asynchronously and render it in the appropriate place in the Matches page using some simple jQuery code.

Using the RenderPage Method to Refresh Part of a Page

At the moment, the Matches page consists of a cshtml file that references the master page named _SiteLayout.cshtml. You will recall from Exercise 1 that the _SiteLayout.cshtml page contains a call to the RenderBody method that causes the Matches.cshtml page to generate its output within the HTML generated by the _SiteLayout.cshtml page.

You can do much the same to render individual parts of a page from a separate file that itself generates HTML that appears in the hosting page. This means that the hosted page can be reloaded without reloading the page that hosts it. The following schematic shows the approach you will take with the Matches page.

Hh145664.0c667790-21ac-4fc3-a38c-b850851823fe(en-us,VS.99).png

You can see that the Matches.cshtml page now contains a call to the RenderPage method, specifying the file LeagueTable.cshtml that generates the HTML table for the league positions and results. As before, the section of the page containing the league table provides a drop-down list of available year numbers. However, now when a visitor makes a selection in this list, the code simply reloads and re-renders the LeagueTable.cshtml page, not the entire page as it did in the example at the end of the previous task. This means that only the section containing the league table changes, and there is no "flicker" or delay as the page loads like that you encountered at the end of the previous task.

The Steps to Implement Background Loading for the League Table

Now that you understand the approach you will use to implement the asynchronous page refresh, you can get to work implementing it in your site.

To implement background loading for the league table

  1. Continue with the SoccerWebsite site you created in the previous task.
  2. In the file list tree view, right-click on the top-level SoccerWebsite item and then click New File. In the Choose a File Type window that opens, select CSHTML and change the name to LeagueTable.cshtml.

    Hh145664.25132c98-7a3d-4b9f-8ca2-1836b05c38de(en-us,VS.99).png

  3. Click OK to add the new page to your site. It automatically opens in the WebMatrix editor pane.
  4. By default, a CSHTML page contains the outline HTML content required for a web page. This includes the DOCTYPE definition at the top and the <html>, <head>, and <body> elements. None of this is required in a partial page such as that you are creating; so, in the editor pane, select all this default content in the new LeagueTable.cshtml page and delete it.
  5. Add the following code section to the LeagueTable.cshtml page to specify that it does not use the site layout master page (it is just content that will be rendered within the Matches page, which itself uses the _SiteLayout.cshtml master page).
    @{
      Layout = null;
            
    }
    
    Hh145664.note(en-us,VS.99).gifNote:
    Now you must move or copy some of the code and content from the Matches page into the new LeagueTable page. The easiest way is to open both files in the editor and cut, copy, and paste the code and HTML from one to the other.

  6. In the files tree view, double-click the file named Matches.cshtml to open it in the editor.
  7. Select the following line in the Matches.cshtml page and press CTRL-C to copy it. Switch to the LeageTable.cshtml page and press CTRL-V to paste it into the page, placing it within the code section you added in the previous step and just before the closing curly brace.
    var db = Database.Open("StarterSite"); 
    
    Hh145664.note(en-us,VS.99).gifNote:
    This line must appear in both pages, so you must copy it, not cut and paste it.

  8. Go back to the Matches page and select all of the code in the code block after the line that declares the variable named upcomingMatches and executes the query to populate it. The code you should select is shown below. It starts on line 13 and continues through to line 31.
      // Define a variable for the league table year to display by default. 
      // Use the value posted from the form if available, or the default if not.
      // NB: In the real world application, you would use the following to generate 
      // the value for the current year instead of using the fixed value 2011:
      // int leagueDisplayYear 
      //     = Request["leagueTableSelector"].AsInt(DateTime.Today.Year);
      int leagueDisplayYear = Request["leagueTableSelector"].AsInt(2011);
    
      // Get a list of league table rows for the selected year sorted in descending 
      // order by points, and then by the number of matches won/drawn/played.
      // Note the use of the replaceable parameter @0 in the SQL statement.
      var leagueTable = db.Query("SELECT * FROM LeagueTable WHERE LeagueYear = @0 " 
        + "ORDER BY LeagueTotalPoints DESC, LeagueMatchesWon DESC, "
        + "LeagueMatchesDrawn DESC, LeagueMatchesPlayed DESC", leagueDisplayYear);
    
      // Get a list of just the unique year numbers in the LeagueTable table rows.
      // This list is used to create the drop-down year selector for the table.
      var leagueYears = db.Query("SELECT DISTINCT LeagueYear As YearNumber "
        + "FROM LeagueTable ORDER BY LeagueYear"); 
    
  9. Press CTRL-X to cut this code from the Matches page. Switch to the LeagueTable page and press CTRL-V to paste it into the code block, placing it just before the closing curly brace.
    Hh145664.note(en-us,VS.99).gifNote:
    The Matches page only requires code that opens the database and selects the set of upcoming matches. The new LeagueTable page requires code that opens the database, extracts the selected year number from the request, creates the list of league table rows, and creates the list of unique year numbers.

  10. Now you can move the HTML that generates the table from the Matches page to the LeagueTable page. In the Matches page, select all of the lines within the <div> element that has the attribute class="floatRightSection". This is the entire HTML table starting with the line <table class="htmlTableList" and continuing through to the line </table>, as shown in the following highlighted code.
    <form id="leagueTableForm" action="@Request.Path" method="get">
      <div class="floatRightSection">
        <table class="htmlTableList"
          ...
          ...
        </table>
      </div> 
    </form>
    
  11. Press CTRL-X to cut this from the Matches page, switch to the LeagueTable page, and press CTRL-V to paste it into the page after the closing curly brace of the code block.
  12. Go back to the Matches page and select the line that declares the opening <form> tag (it's just before the <div class="floatLeftSection"> opening tag). Press CTRL-X to cut this from the Matches page, switch to the LeagueTable page, and press CTRL-V to paste it into the page immediately before the <table> you just inserted as shown in the following highlighted code.
    }
    <form id="leagueTableForm" action="@Request.Path" method="get">
      <table class="htmlTableList"
          ...
    
  13. Go back to the Matches page and select the line that declares the closing </form> tag (it's just after the closing <div> tag). Press CTRL-X to cut this from the Matches page, switch to the LeagueTable page, and press CTRL-V to paste it into the page immediately after the </table> closing tag as shown in the following highlighted code.
    }
    <form id="leagueTableForm" action="@Request.Path" method="get">
      <table class="htmlTableList"
        ...
        ...
      </table>
    </form>
    
  14. An HTML <form> section should have a <div> section as its single child element (this <div> section will contain all of the other elements in the form). In the LeagueTable page, add a new <div> element around the <table> and inside the <form> as shown in the following highlighted code.
    <form id="leagueTableForm" action="@Request.Path" method="get">
      <div>
        <table class="htmlTableList"
          ...
          ...
        </table>
      </div>
    </form>
    
  15. The final modification to the existing code you copied into the LeagueTable page is to prevent changes to the value in the drop-down list of years from submitting the page to the server. This was required in the previous task, but in this task you will refresh the page by loading it using some jQuery code. Find the <select> element in the LeagueTable page and delete the onchange attribute and its value (but not the closing ">" character). The following shows how this section of the page should look afterwards.
      ...
      <select id="leagueTableSelector" name="leagueTableSelector" 
              class="tableSelectList">
      @foreach (var yearItem in leagueYears)
      ...
    
    Hh145664.note(en-us,VS.99).gifNote:
    You have now transferred the code and HTML that creates the league table to the new LeagueTable.cshtml page. If you want to see what the LeagueTable page actually generates, select it in the file tree view and click the Run icon on the toolbar ribbon. You will see the table for the default year displayed with no styling or layout information, and you won't be able to change the displayed table to see a different year, but it demonstrates how you can generate separate sections of content for a page.

  16. Next, you must add code to the Matches page that causes it to display the LeagueTable partial page, and reload it when a visitor changes the selected year in the drop-down list defined in the LeagueTable page. In the Matches.cshtml page, add the following highlighted id attribute to the <div> element that carries the attribute class="floatRightSection".
    <div id="leagueTableContainer" class="floatRightSection">
    
    Hh145664.note(en-us,VS.99).gifNote:
    The code that refreshes this part of the page will load the update league table into this element. In other words, it will replace the existing child element and all its child and descendent elements with the HTML generated by the LeagueTable partial page.

  17. Now add the following highlighted code within this <div> element to force the LeagueTable page content to be rendered within the <div> when the Matches page is first loaded, and the JavaScript code section after it that reloads the contents of the <div> when the user selects a different year in the drop-down list.
    <div id="leagueTableContainer" class="floatRightSection">
      @RenderPage("LeagueTable.cshtml")
    </div>
    <script type="text/javascript">
      $(document).ready(function() 
      {
        $("#leagueTableSelector").live('change', function() 
        {            
          $("#leagueTableContainer").load(
              '/LeagueTable.cshtml?leagueTableSelector=' + $(this).val());
        });
      });
    </script>
    
    Hh145664.note(en-us,VS.99).gifNote:
    This code replaces the <table> and other content that you used in the previous task to display the league table data. It calls the server-side RenderPage method, and then adds a client-side JavaScript code section that uses the jQuery library routines. The server will insert the output from the LeagueTable.cshtml page into the output generated by the Matches.cshtml page at this location. When the browser loads the page, it will appear to be a single page, and so the JavaScript code can reference elements in the both the Matches.cshtml and LeagueTable.cshtml files.
    The JavaScript code binds a function to the change event of the <select> element with ID leagueTableSelector. This function calls the load method of the <div> element with ID leagueTableContainer (which is the root element in the LeagueTable.cshtml page) when a visitor changes the selected year, instructing it to load the page /LeagueTable.cshtml with a name/value pair in the query string. This name/value pair consists of the name of the <select> list (leagueTableSelector) and its currently selected value; for example, leagueTableSelector= 2010. This is, of course, exactly the same query string as was sent by the page in the previous task of the exercise when a visitor submitted the form that contains the league table and the year selector drop-down list.

  18. Save the Matches.cshtml and LeagueTable.cshtml pages. Then select Matches.cshtml in the left-hand tree view and click the Run icon on the toolbar ribbon. You can see that it looks exactly the same as it did at the end of the previous task. This is the desired result, because all you have done is moved the league table generation into a separate page and not changed the way it generates the output.

    Hh145664.6820d05c-cc84-4aee-b37b-6e547f88b1a7(en-us,VS.99).png

  19. Change the selected year to 2010 in the drop-down list. You will see that the league table section refreshes to show the new data, but the page itself does not flicker. It is not being reloaded from the server. The user experience is certainly a lot better than it was before!
  20. But what about if the visitor has disabled scripting in their browser, or they are using a specialist or non-visual user agent that doesn't support client-side script? Use the options in your web browser to disable script execution:
    1. In Internet Explorer, open the Tools menu and click Internet Options. Open the Security tab and click the Custom Level button. Scroll down though the options that are shown to find the Scripting section, and change the Active Scripting option to Disable. Then click OK until you get back to the main browser window.
    2. In Firefox, open the Tools menu and click Options. In the Options dialog click the Content icon and uncheck the Enable JavaScript option. Then click OK.
  21. Now refresh the page in your browser (don’t just change the year as this does not reload the entire page). You will see that the Go button is now visible next to the drop-down list. Select a different year and click the Go button to prove that it still works just as it did in the previous task of this exercise.

    Hh145664.25942233-00da-424c-918b-d1321e577e2a(en-us,VS.99).png

    Hh145664.note(en-us,VS.99).gifNote:
    This works because you transferred the <form> tags and the <noscript> element containing the submit button to the LeagueTable page, along with the rest of the code that generates the league table. This code still uses the @Request.Path property to create the value for the action attribute of the form, but this is OK because the actual URL of the resulting page that the browser loads is Matches.cshtml (not LeagueTable.cshtml). You can see this if you view the source of the page in your browser, and in the browser's address bar, even after it refreshes the league table section by reloading the partial page LeagueTable.cshtml.

  22. Change the selected month and click Go a couple more times. You probably noticed that, with scripting disabled, the browser reloads the entire page instead of refreshing just the league table. This occurs because the code now submits the year number to the server in the query string of a GET request for the page Matches.cshtml, instead of using the jQuery library functions to fetch the generated output asynchronously from the page LeagueTable.cshtml and load it into the existing page. In fact, for non-visual browser, this is actually an advantage as they tend to be less reliable at detecting changes to a page after they have loaded it. Reloading the entire page allows these types of browser to reparse the page and take the changed content into account.
  23. Change the settings in your browser to re-enable script execution before you finish this exercise.

You have now completed this task and this exercise. You should now be comfortable with the ways that you can present tabular information in your pages, including additional elements that provide extra information for non-visual user agents, specialist browsers, and automated page reading tools. You should also understand how you can use HTML forms to send information to the server, and you have started to see how you can process this information in your pages. Finally, you should understand how you can implement asynchronous background content refresh actions to update parts of a page without reloading the entire page every time a visitor interacts with it.



Show: