In this chapter, we focus on the user interface. In the first part of this chapter, we add support for paging through blog entries. Next, we create the views for our blog application. Finally, we modify our blog application to take advantage of Ajax.
We have only two user stories that we need to implement (see Figure 20.1). One story for paging and one story for Ajax.
Our first user story concerns paging. We don’t want users of the blog application to get overwhelmed with too many blog entries displayed in a page at one time. According to the user story:
No more than five blog entries should be displayed on a page at a time. When there are more than five entries, the user should navigate to earlier entries.
In Chapter 6, “Understanding HTML Helpers,” we implemented a simple paging framework. We can take advantage of that framework here in our blog application.
In the paging framework, we use the class in Listing 20.1 to represent a page of items.
The nice thing about the PagedList
class in Listing 20.1 is that it represents not only a page of items, but it also represents additional properties such as the total page count and the page size.
We want to modify our blog application to use this paging framework. However, before we can start rewriting our application code, we first need to create some tests.
Here’s what we want to test:
Index()
action should accept a page
parameter.Index()
action exposed by the Blog
controller should return an instance of the PagedList
class that corresponds to the page
parameter.PagedList
returned by the Blog
Index() action should never contain more than five blog entries.PageList
returned by the Blog
Index() action should order the blog entries by date (latest blog entry first).This list gives us the four tests that are contained in Listing 20.2.
When we first attempt to run the tests in Listing 20.2, they fail. The tests won’t even build because the Blog
controller Index()
action does not currently accept a page
parameter (see Figure 20.2).
Before we do anything else, we need to write just enough code so that our solution compiles. We need to modify the Blog
controller Index()
action so that it accepts a page
parameter and returns a PagedList
. The modified Index()
action is contained in Listing 20.3.
Unfortunately, modifying the Index()
action introduces new problems with our test code. Three tests that passed before now fail because we have modified our Index()
action (see Figure 20.3).
This problem is easy enough to fix. We just need to update the old tests so that the tests pass a NULL
value when calling the Index()
action. After we make this change to the test code, the first of our four new tests pass.
To get the next test to pass, we need to modify our application code so that the Index()
action returns a PagedList
. However, it cannot be just any PagedList
. The PagedList
must represent the set of blog entries that correspond to the page passed to the Index()
action.
The modified Index()
action in Listing 20.4 passes the page
parameter to the ListBlogEntries()
method.
We need to modify the ListBlogEntries()
method so that it accepts a page
parameter (see Listing 20.5).
Finally, we need to modify the Blog
repository so that it returns the records that correspond to a particular page. The updated Blog
repository ListBlogEntries()
method is contained in Listing 20.6.
In Listing 20.6, the ListBlogEntries()
method has been modified to return a PagedList
class. Notice the final statement. The final statement calls the ToPagedList()
extension method (defined in the PagingLinqExtensions
class) to return a PagedList
from a query.
After we make all these changes, all our tests pass except for one (see Figure 20.4). Unfortunately, our blog entries are not returned in the right order. We want to display the blog entry with the latest publication date first.
We need to make one last change to our Blog
repository ListBlogEntries()
method. We need to order the blog entries by the date published. We can do this by calling the OrderByDescending()
LINQ method on our query (see Listing 20.7). Finally, all the tests pass (see Figure 20.5).
It is finally time to improve the appearance of our blog application. In this section, we create the views and partials for our blog.
Let’s start with the default page—the page generated by the view returned by the Blog
controller Index()
action. This view displays the list of blog entries (see Figure 20.6).
Because we changed the view data model returned by the Blog
controller Index()
action, we need to delete the existing Index
view and re-create it with the correct view data model class. Replace the existing Index
view with the Index
view in Listing 20.8.
Notice that the view in Listing 20.8 has an Inherits
attribute that causes the view data model to be cast to a PagedList<BlogEntry>
class.
The Index
view delegates all its blog entry rendering work to a partial named BlogEntries
. The BlogEntries
partial renders all the blog entries and the pager
user interface.
The BlogEntries
partial is used by both the Blog
Index view and the Archive
Index view. By creating a separate partial, we’ve eliminated duplicate user interface content among the views in our application.
The BlogEntries
partial is contained in Listing 20.9.
Notice that the BlogEntries
partial uses two custom HTML helpers. First, the Html.BlogLink()
helper renders a link to a blog entry. This helper is contained in Listing 20.10.
To avoid registering the namespace for the HTML helpers in every page in which I use the helpers, I registered the namespace in the web.config file like this:
A second HTML helper renders the pager
user interface (see Listing 20.11). This helper renders an Older Entries
link when there are older entries and a Newer Entries
link when there are newer entries. If there isn’t more than a single page of blog entries, the helper renders nothing at all.
I love my iPhone. The iPhone was designed to provide a great user experience. The iPhone has a number of user interface design innovations. One innovation concerns how paging is implemented.
Earlier in this chapter, we implemented a standard user interface for paging. We created Older
Entries and Newer
Entries links. When you first request the page, the first five blog entries display. When you click the Older
Entries link, the first blog entries are replaced with the next blog entries.
In this section, we change our user interface for paging to take advantage of Ajax. Instead of displaying Older
Entries and Newer
Entries links, we only display a More Entries
link. When you click the More
Entries link, additional blog entries are appended to the first five blog entries. The list of blog entries grows instead of being replaced (see Figure 20.7).
Even after we implement Ajax paging, our blog application continues to work with JavaScript disabled. When JavaScript is disabled, the blog application does a normal post to the server to retrieve the next set of five blog entries.
In this section, we improve the user experience and responsiveness of our blog application by adding support for Ajax. We focus on implementing the following user story:
When there are more than five entries, a More Entries
link displays. When you click the More Entries
link, five more blog entries are appended to the list of blog entries (see Figure 20.8).
As always, we start with a test. There are now two ways that we want to retrieve blog entries: with a normal page request and with an Ajax request. In the first case, we want to return a ViewResult
, and in the second case we want to return a PartialViewResult
. The test in Listing 20.12 verifies that we can invoke an action that returns a PartialViewResult
.
When we first attempt to run the test in Listing 20.12, it fails because the Blog
controller does not have an Index_Ajax()
action. Because we have a test, we can allow ourselves to add this new action (see Listing 20.13).
The Index_Ajax()
action in Listing 20.13 is exactly the same as the Index()
action, except the Index_Ajax()
action returns a PartialViewResult
instead of a normal ViewResult
.
Notice that the Index_Ajax()
action is decorated with two attributes. The AcceptAjax
attribute causes this action to be called only in the context of an Ajax request. This attribute is not a standard part of the ASP.NET MVC framework. We discussed this attribute in Chapter 14, “Working with Ajax.”
The second attribute, the ActionName
attribute, exposes the method as an action with a different name than the method name. This attribute causes the Index_Ajax()
method to be exposed as the Index()
action.
To take advantage of the new Index_Ajax()
action, we need to modify our views. Listing 20.14 contains the updated Blog
controller Index
view.
Notice that the view in Listing 20.14 includes a loadingMoreEntries
DIV element. The contents of this DIV element display while the blog application waits for the results of an Ajax request.
I downloaded the busy wait image from www.ajaxload.info. This website enables you to generate different types of busy wait images and use them in your applications for free.
The updated BlogEntries
partial is contained in Listing 20.15.
Only one change has been made to the partial in Listing 20.15. The BlogEntries
partial now uses an Ajax.BlogPager()
instead of the Html.BlogPager()
.
The Ajax.BlogPager()
is contained in Listing 20.16.
The Ajax.BlogPager()
calls the Ajax.ActionLink()
helper method internally to render the More
Entries link. An instance of the AjaxOptions
class is created to specify the behavior of the Ajax.ActionLink()
. The AjaxOptions
class causes the link to update the blogEntries
DIV tag with the results of the Ajax request.
After we make these changes, our blog application now supports Ajax. When we page through the blog entries by clicking the More
Entries link, additional blog entries are appended to the existing page.
In this chapter, we focused on improving the user interface of our blog application. In the first part of this chapter, we added support for paging through blog entries. We took advantage of the paging framework that we created in Chapter 6.
Next, we rewrote the views and partials for our blog application. Our views share a single partial that displays the list of blog entries. That way, we can control the appearance of our blog entries by modifying a single file.
Finally, we added Ajax support to our blog application. We created an Ajax pager that enables us to continuously grow our page by appending new blog entries to the existing blog entries.
18.188.242.160