Tag Archives: view model

MVC: user input model

In the last post, we saw how restructuring the code so that a view model is used to prepare the data for the view helps decouple the view from the calculations required to process the data before displaying it. In this post, we’ll have a look at how to apply a similar approach to user input.

We’ll modify the ComicShop site by adding in a boolean parameter specifying whether we’ve read a particular comic. To do this, we’ll need to modify the underlying database by adding a Read field. You can do this by opening the database in Visual Studio’s Server Explorer (double-click on the database file in Solution Explorer to do this), then navigating to the Books table, then right-clicking and selecting Edit Table Schema. Add in a column called Read, and set its type to ‘bit’. Set its default value to 0.

We also need to modify the Book class by adding a bool property called Read.

To make things easier in what follows, we will also modify the home page so that it displays the Read status of each comic book. We can do this by modifying the IndexModel.cshtml code to this:

@using ComicShop.Models;
@model List<Book>

<h2>Comics</h2>
<p><a href="/Home/Add">Add new comic</a></p>
<p><a href="/Home/Summary">Summary</a></p>
<p><a href="/Home/Read">Comics read</a></p>

<ul>
    @foreach (var comic in @Model)
    {
        <li>
                @comic.Title: <b>@comic.Volume</b> (@comic.Issue) [@(comic.Read ? "Read" : "Unread")]
        </li>
    }
</ul>

Now we’re ready to add a page on which the user can change the Read status of one or more comics. We’d like to display a table of the comics in the database, with each row in the table showing the title, volume and issue of the comic, and a checkbox allowing the Read status to be edited. The controller method for this is very simple:

    public ViewResult Read()
    {
      var model = comicRepository.GetComicList();
      return View(model);
    }

Before we construct the view, we need to stop and think about what the Read view will return. All we really need to identify the read status of a comic is the comic’s primary key (the Id column in the database) and the read status itself. The input model for this page is then the simple class:

namespace ComicShop.Models
{
  public class ComicReadStatus
  {
    public int Id { get; set; }
    public bool Read { get; set; }
  }
}

This just uses the same code we had before, since the view requires the same data as the home page. The Read.cshtml file looks like this:

@using ComicShop.Models;
@model IEnumerable<Book>

@{
    ViewBag.Title = "Read";
}

<h2>Comics Read</h2>
<div>
    <form action="@Url.Action("UpdateReadStatus")" method="post">
        <table>
            <tr>
                <th>Title</th>
                <th>Volume</th>
                <th>Issue</th>
                <th>Read?</th>
            </tr>
            @{ int index = 0; }
            @foreach (var comic in Model)
            {
                <tr>
                    <td>@comic.Title</td>
                    <td>@comic.Volume</td>
                    <td>@comic.Issue</td>
                    <td>
                        @Html.CheckBox("comicRead[" + index + "].Read", comic.Read)
                        <input name="@("comicRead[" + index + "].Id")" value="@comic.Id" type="hidden" />
                    </td>
                </tr>
                index++;
            }
        </table>
        <button name="submit">Update</button>
    </form>
</div>

The page constructs a table in the same way as we did for the ComicSummary page in the last post. The one notable feature here is the addition of the checkbox. Rather than use bare HTML for this, we’ve used one of MVC’s Html helper functions. This function takes 2 parameters; the first is the name of the checkbox and the second is its initial value.

Since we’re displaying a table of comics, there will be a checkbox for each comic, so we name the checkbox in such a way that the table builds an array. The name of the checkbox is translated dynamically into a data type, so we build up the array by defining an ‘index’ parameter to number the rows in the table and insert this into the ‘comicRead’ array for each row in the table. It’s important here to make sure that the properties of the array are the same as those in the data type we are to export from the form. In this case, we’ll be exporting a list of ComicReadStatus objects, so each array element must have a bool Read and an int Id property. The Read property will be set to the value in the checkbox when the form’s submit button is pressed.

If you’re familiar with basic HTML, you might know that a form returns a value for the checkbox only if it is checked, so that if the checkbox is cleared, there would be no corresponding data sent in the post from the form. The Html.Checkbox() function gets around this problem by defining an additional hidden HTML control with the same name as the checkbox, and a permanent value of ‘false’. If the checkbox is true, it overrides this hidden field, but if the checkbox is false it is ignored and the hidden field then gets sent back from the form. Thus we will have a definite value for each checkbox whether it is true or false.

The explicit hidden field in the Read.cshtml code passes the comic’s Id value back from the form. Thus the output of the form is a list of ComicReadStatus objects, each of which contains a primary key of a comic and its Read value as specified by the user on the form.

In the form definition on line 10, we see that the action called by submitting the form is UpdateReadStatus, so we’ll need to write that before we try to use the form (although you can run the site now and go to the /Home/Read page to see the form if you like).

The action method in HomeController is again very simple:

    public ActionResult UpdateReadStatus(List<ComicReadStatus> comicRead)
    {
      comicRepository.UpdateReadStatus(comicRead);
      return RedirectToAction("Index");
    }

The input parameter is a List<ComicReadStatus>. You might wonder how the program knows to convert the data sent back from the form into this structure. This is a feature of MVC’s model binding technology, and for now it’s probably safer just to accept that it works. One thing is important though: you must ensure that the name of the parameter (‘comicRead’ here) is the same as that of the array you defined in Read.cshtml’s checkbox. Unlike the usual C# parameter names, where the parameter in the function definition doesn’t have to match that of the variable that is sent into the function, here it does matter, and if you make the names different, the object received by the action method is null (with all the pain that that generates when you try to run the method).

We’ve delegated the work done by UpdateReadStatus() to a method called UpdateReadStatus() in the ComicRepository. This allows us to decouple the controller from the access to the data source, as we’ve done so far in order to enable unit testing. The last line of the action method redirects the browser back to the home page where you can see the results of your edits.

In the ComicRepository class that we’ve been using for interaction with the database, we therefore need to write some code which saves any changes made by the user back to the database. Herein lies a bit of a problem, since the Read view has no connection with, or knowledge of, where its data comes from (which is correct). However, as such, there’s no way to tell from the data sent back by the form which comics have had their Read status changed. There’s really only one way we can be sure of saving all the changes to the database: we’ll have to iterate over all the comics that were listed on the Read view and check to see which ones have had their Read status changed. This obviously isn’t very efficient, but the only other way of doing this involves responding directly to the user’s clicks on the Read page, and that will take us too far afield. So we’ll content ourselves with a fairly brute force method for updating the database. (Actually, a proper view wouldn’t have that many lines in a table anyway, so this probably isn’t all that inefficient, but still…)

Here’s the code in the ComicRepository class:

    public void UpdateReadStatus(List<ComicReadStatus> comicReadStatus)
    {
      foreach (var comic in comicReadStatus)
      {
        Book book = database.ComicBooks.Find(comic.Id);
        if (book.Read != comic.Read)
        {
          book.Read = comic.Read;
          database.Entry(book).State = System.Data.EntityState.Modified;
        }
        database.SaveChanges();
      }
    }

For each comic in the list, we use the DbSet’s Find() method to look up the full comic object in the database. Remember that the comicReadStatus list contains only ComicReadStatus objects, which contain only the Id and Read fields for a given comic book. The Find() method uses an object’s primary key to look it up in the database and then returns the full object.

We then check to see if the Read field has changed and if so, we change the Read field in the full Book object, and then mark this object’s State as Modified. Finally SaveChanges() will save all the rows that have been marked as modified.

Advertisements

MVC: the view model

The ComicShop application we’ve presented so far has been quite basic, in that it does nothing more than allow the user to store comics in a local database, and display a list of these comics on the home page. In the process of creating the application, though, we’ve shown the division of labour between the model (where the data are stored and manipulated), the controller (which handles requests from the user and decides which view to display), and the view (which creates the actual HTML that is sent back to the browser).

You might think that, having covered all three components for which MVC is named, we’re finished with the basics. However, there is another logical component that forms part of the design when the application becomes anything more than very simple. This is the view model. (The view model occupies a more central position in another design system known as MVVM (thanks Stuart!), but we’ll leave a discussion of that until later.)

To illustrate the need for a view model (and to explain what it is), suppose we wanted to add a web page that displayed a summary of the comics in the database by listing each unique title followed by the number of comics of that title in the database. Ultimately, we’d like to display this information as a table in HTML, where each row in the table consists of a string (the title) and an int (the count for that title). To obtain this information, we need to query the database and do a count of each title returned. Where should this work be done?

One option is that the controller queries the database for a complete list of comics (as it does for the page that displays the complete list), and sends this list as is to the view, leaving the view to count up the number of comics for each title and then display the results. This isn’t very efficient, since the view is being forced to do a lot of stuff that has nothing to do with the display of the page.

Another option is that the controller does the calculations itself and then packages up the results and sends them to the view for display. That seems a little better, since at least the view is being cluttered with calculations. However, it isn’t really the job of the controller either, since its job is mainly to route the page request from the user to the correct view.

The solution currently accepted as best practice is to have the controller create a view model which is then sent to the view. The view model is a mirror in C# code of the data that the view requires in order to produce the HTML. In the case of our ComicShop summary page, we’d like a list of rows for the table, where each row contains the title and count for a group of comics. We therefore create a class representing a single row in the table, which looks like this:

namespace ComicShop.Models
{
  public class ComicSummary
  {
    public string Title { get; set; }
    public int Count { get; set; }
  }
}

The idea is now to have the controller construct the view model as an IEnumerable<ComicSummary> list (that is, a collection of ComicSummary objects that implements the IEnumerable interface), and that is what gets sent to the view. This list is the view model for the view that displays the summary information. It contains only the information that the view needs, and doesn’t require the view to do any further calculation.

Where should this list be constructed? To keep the controller clean, the construction shouldn’t be done directly inside the controller. Farming the process out to a separate class also fits in with the design we adopted when implementing unit testing, since we’d like to be able to test the summary-generation code in the controller without directly accessing the database. A sensible place to put the list-building code, then, is in the classes (one for testing and one for running normally) that implement the IComicRepository interface that we designed in the post on unit testing.

When we’re extracting the information from the database, we can actually do all the calculations required using LINQ. Here’s the method in the ComicRepository class:

    public ComicContext database = new ComicContext("ComicContextDb");
    public IEnumerable<ComicSummary> GetSummaries()
    {
      var summaries = database.ComicBooks.Select(book => book).OrderBy(book => book.Title).
        GroupBy(comic => comic.Title,
        (title, titleGroup) => new ComicSummary
        {
          Title = title,
          Count = titleGroup.Count()
        }
        );
      return  summaries;
    }

The first line in the method is the LINQ code that selects all the comics, orders them by title, and then applies the GroupBy() method to sort them into groups where the key to each group is the Title field. The second argument to GroupBy() is a function which takes two parameters: the first is the title of the comic, and the second (titleGroup) is the group of comics with that title. We then construct a ComicSummary object containing the title and the count of comics in that group. The final returned object is the required IEnumerable<ComicSummary> list.

Back in the HomeController class, we add an action for the Summary page:

    public ViewResult Summary()
    {
      IEnumerable<ComicSummary> summaries = comicRepository.GetSummaries();
      return View(summaries);
    }

(The comicRepository object is initialized in the constructor as before.) The controller code is still very clean, since the work of building the view model is done in ComicRepository.

Finally, the view for the Summary page is also very simple:

@using ComicShop.Models
@model IEnumerable<ComicSummary>

<div>
    <h2>Comic summary</h2>
    <table>
        <tr>
            <th>Title</th>
            <th>Count</th>
        </tr>
        @foreach (var summary in Model)
        {
            <tr>
                <td>@summary.Title</td>
                <td>@summary.Count</td>
            </tr>
        }
    </table>
</div>

The model directive at the top is set to an IEnumerable<ComicSummary> data type, making this a strongly typed view. The rest of the view builds the table by iterating over the elements in the model.

Finally, we can add a GetSummaries() method to our test repository class ComicRepositoryTest. It might look something like this:

    public IEnumerable<ComicSummary> GetSummaries()
    {
      return new[]
      {
        new ComicSummary
        {
          Title = "Superman",
          Count = 3
        },
        new ComicSummary
        {
          Title = "Thor",
          Count = 12
        }
      };
    }

We manually add a couple of ComicSummary objects to a list and return it. To use this, we can add some tests to HomeControllerTests. For example, we can test that the number of entries in the table is 2:

    [TestMethod]
    public void Summary_Count()
    {
      HomeController homeController = new HomeController(
        new ComicRepositoryTest());
      var summary = homeController.Summary();
      var table = summary.Model as ICollection<ComicSummary>;
      Assert.IsTrue(table.Count == 2, "Count is {0}", table.Count);
    }

Since IEnumerable <T> doesn’t support a Count property, we’ve cast the Model field of ‘summary’ to an ICollection, which does have a Count. ICollection inherits IEnumerable, so the cast is valid providing the underlying object implements ICollection, which in this case it does.