DOC

ASP.NET MVC2 IN ACTION chapter3

By Heather Wallace,2014-06-27 04:04
12 views 0
ASP.NET MVC2 IN ACTION chapter3

Ch3_ Palermo _ ASP.NET_toProd

    3

    View fundamentals This chapter covers

    ? Providing data to the view

    ? Using strongly typed views

    ? Understanding view helper objects

    ? Developing with templates

    The view’s responsibility can be deceptively simple. Its goal in life is to take the model given

    to it and use it to render content. Because the controller and related services already

    executed all the business logic and packaged the results into a model object, the view only

    needs to know how to take that model and turn it into HTML. Although this separation of

    concerns removes much of the responsibility that can plague traditional ASP.NET applications,

    views still need to be carefully and deliberately designed. Views require knowledge and

    understanding of the building blocks of the web, including HTML, CSS, and JavaScript.

    In this chapter, we’ll examine how ASP.NET MVC renders views, how the default WebFormViewEngine functions, and how to structure and organize views. Then we’ll look at

    a couple of approaches for using the model to render content in a view. Finally, we’ll cover

    the templating features new to ASP.NET MVC 2.

    3.1 Introducing views

    A view’s responsibility is to render content. But how does the MVC framework decide which

    view to use? How do we control what gets rendered, and how do we organize our content?

    How do we even tell MVC to render a view? Ch3_ Palermo _ ASP.NET_toProd In the ASP.NET MVC framework, the controller decides, based on user input, that a view

    should be rendered by returning a ViewResult object from a controller action. Listing 3.1

    shows an action returning a ViewResult.

Ch3_ Palermo _ ASP.NET_toProd [Authorize] public ActionResult ChangePassword() { return View(); }

    Although the method name seems to indicate that a view is rendered as the result of

    calling the View method, it’s merely a helper method in the Controller base class to

    create a ViewResult object. The ViewResult object contains all the information needed to

    render the view at a later time. This information includes the view name, the model, and

    other pertinent information an IViewEngine can use to render a view.

    Internally, the ViewResult object delegates to the IViewEngine to render the content

    for a view. The IViewEngine implementation, commonly just called the view engine, is the class responsible for examining the ViewResult information as well as other context

    information and for locating the correct IView to render.

    3.2 Examining the ViewDataDictionary

    The main object used to pass model information to a view is the ViewDataDictionary

    class. Like other MVC frameworks, ASP.NET MVC exposes a dictionary to enable the

    controller action to pass any number of model objects and information to the view. With a

    dictionary object, we can pass as many items as need be for the view to render appropriately.

    For example, consider a profile page where users can view other users’ profiles, but only

    the currently logged-in user can edit their profile. To display the profile information on the

    profile screen, we can pass in the Profile object, shown in listing 3.2, directly to the view.

    public class Profile { public Profile(string username) { Username = username; } public string Username { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Email { get; set; } }

    Although our

    Profile class has all the information needed to display our Profile, it

    doesn’t include any information about the currently logged-in user, or specify whether the

    view should display the Edit link. We need to give the view more information than solely the

    Profile object to make this decision. We can use the ViewDataDictionary to provide

    this extra piece of information, as shown in listing 3.3.

    public ViewResult Show(string username) Ch3_ Palermo _ ASP.NET_toProd

Ch3_ Palermo _ ASP.NET_toProd

    { var profile = _profileRepository.Find(username); bool hasPermission = User.Identity.Name == username; Controller base class, we have access to the ViewDataDictionary object passed to the view in the ViewData property. We check the current user’s name, compare it ViewData["hasPermission"] = hasPermission; to the profile to be shown in the username parameter, and place the result of the return View(profile); comparison into ViewData with a hasPermission key. Next, we use the helper View } method to create a In the ViewResult object and set the ViewData’s Model property to our Profile object.

    On the view side, we’ll pull the hasPermission information out of ViewData and use it

    to hide the Edit link, as shown in listing 3.4.

    <p> <% bool hasPermission = |#1 (bool)ViewData["hasPermission"]; |#1 if (hasPermission) { %> <%=Html.ActionLink("Edit", "Edit", |#2 new { username = Model.Username }) %> |#2 | <%=Html.ActionLink("Back to List", "Index") %> |#3 <% } %> </p>

    Cueballs in text.

    In our view, we extract the hasPermission information (#1) from ViewData. Next, we

    conditionally show the Edit link based on the hasPermission variable (#2). Finally, we

    display a link (#3) to take the user back to the profile list page. The final rendered page for

    showing the current user’s profile is shown in figure 3.1.

    Ch3_ Palermo _ ASP.NET_toProd

    Ch3_ Palermo _ ASP.NET_toProd

    Figure 3.1 The current user’s profile page

    ViewDataDictionary gives us a The technique of utilizing the dictionary aspects of the lot of flexibility, but it comes at a price. Because we create weak, compile-unsafe links in a

    dictionary, we open ourselves to problems in the future. For example, we might misspell

    hasPermission in the view, and only learn of our mistake at runtime. But our use of the

    Profile object as our view model gives us a strong link between controller action and view,

    compile-time safety, and IntelliSense in the view.

    Using the loose-type semantics of a dictionary can also hinder us in more complex

    scenarios. Consider a login screen where the username and password are required fields.

    With an object to represent the model for this view, we can decorate our view model object

    with validation attributes. In the next section, we’ll look at taking advantage of view model

    types with strongly typed views.

    3.3 Strongly typed views with a view model

    When using the WebFormViewEngine, our views can inherit from two types: System.Web.Mvc.ViewPage or System.Web.Mvc.ViewPage<T>. The generic

    ViewPage<T> inherits from ViewPage but offers some unique additions not available in the Ch3_ Palermo _ ASP.NET_toProd nongeneric ViewPage class.

    ViewPage<T> is shown in listing 3.5. Ch3_ Palermo _ ASP.NET_toProd

    public class ViewPage<TModel> : ViewPage { public AjaxHelper<TModel> Ajax { get; set; } The skeleton member definition of public HtmlHelper<TModel> Html { get; set; } public TModel Model { get; } #1 public ViewDataDictionary<TModel> ViewData { get; set; } }

    #1 Strongly typed view model

    Cueballs in code and text

    In addition to providing a strongly typed wrapper over ViewData.Model through the Model property (#1), the ViewPage<T> class provides access to strongly typed versions of

    the associated view helper objects, AjaxHelper and HtmlHelper.

    To use a strongly typed view, we first have to ensure that our controller action sets the

    ViewData.Model properly. In listing 3.6, we retrieve all the profiles for display in a list

    page and pass the entire collection of profiles to the View method, which encapsulates

    setting the ViewData.Model property.

    public ViewResult Index() { var profiles = _profileRepository.GetAll(); return View(profiles); }

    In the Index view used with this action, even the loose-typed ViewPage class can use

    the ViewData.Model property. But this property is only of type object, and we’d need to

    cast the result to use it effectively. Instead, we can make our view page inherit from

    ViewPage<T>, as shown in listing 3.7.

    <%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<AccountProfile.Models.Profile[]>" %>

    By inheriting from ViewPage<T> instead of ViewPage, we now have a strongly typed

    view. In the next section, we’ll look at how we can use our view model object to display

    information in a view.

    3.4 Displaying view model data in a view

    Typically, to display information in a view, we’ll use the HtmlHelper object to help us use

    our view model to generate HTML. Consider listing 3.8, where we render a collection of

    profiles.

    Ch3_ Palermo _ ASP.NET_toProd

    Ch3_ Palermo _ ASP.NET_toProd <h2>Profiles</h2> <table> <tr> <th>Username</th> <th>First name</th> <th>Last name</th> <th>Email</th> <th>&nbsp;</th> </tr> <% foreach (var profile in Model) { %> #1 <tr> <td> <%= Html.Encode(profile.Username) %> #2 </td> <td> <%= Html.Encode(profile.FirstName) %> </td> <td> <%= Html.Encode(profile.LastName) %> </td> <td> <%= Html.Encode(profile.Email) %> </td> <td> <%= Html.ActionLink("View Profile", "Show", new{username = profile.Username}) %> </td> </tr> <% } %> </table>

    #1 Iterates over all profiles #2 Displays profile information

    Cueballs in code and text

    In our profile list screen, we want to iterate over the profiles passed in our model (#1)

    and display select information from each (#2). Because we’d rather not open ourselves to

    the myriad of scripting attacks possible when displaying unencoded user input to the screen,

    we encode all user-entered information by using the Encode method on HtmlHelper,

    which is exposed through the Html property on our base ViewPage<T> (and ViewPage)

    class.

    In our login page, we use a view model object to represent the entire form, as shown in

    listing 3.9.

    public class LogOnModel { [Required] |#1 [DisplayName("User name")] |#1 public string UserName { get; set; }

    Ch3_ Palermo _ ASP.NET_toProd

Ch3_ Palermo _ ASP.NET_toProd

     [Required] |#1 [DataType(DataType.Password)] |#1 #1 Applies data annotation attributes [DisplayName("Password")] |#1 public string Password { get; set; } Cueballs in code and text public bool RememberMe { get; set; } }

    The LogOnModel class is simple, containing only auto properties. The attributes (#1) you see here are data annotations, and you’ll learn more about them in chapter 4. The logon

    screen shows input elements for each of these properties, as you can see in figure 3.2.

Figure 3.2 The logon screen

    Because we opted for a strongly typed view for our logon screen, we can use the built-in

    helpers to render the HTML for each input element. Instead of loosely bound strings to

    Ch3_ Palermo _ ASP.NET_toProd

Ch3_ Palermo _ ASP.NET_toProd

    HtmlHelper extensions to create various types of input elements, as shown in listing 3.10. represent the action parameters, we can take advantage of the expression-based

    <% using (Html.BeginForm()) { %> <div> <fieldset> <legend>Account Information</legend> <p> <%= Html.LabelFor(m => m.UserName) %> |#1 <%= Html.TextBoxFor(m => m.UserName) %> |#2 <%= Html.ValidationMessageFor( | |#3 m => m.UserName) %> |#3 </p> <p> <%= Html.LabelFor(m => m.Password) %> <%= Html.PasswordFor(m => m.Password) %> <%= Html.ValidationMessageFor(m => m.Password) %> </p> <p> <%= Html.CheckBoxFor(m => m.RememberMe) %> <label class="inline" for="rememberMe">Remember me?</label> </p> <p> <input type="submit" value="Log On" /> </p> </fieldset> </div> <% } %>

    #1 Strongly typed label helper #2 Strongly typed text box #3 Strongly typed validation message

    Cueballs in code and text

    In listing 3.10, we take advantage of several of the HtmlHelper extension methods

    designed for strongly typed view pages, including methods for labels (#1), input text boxes

    (#2), and validation messages (#3). Instead of a loose-typed string to represent properties,

    like those used in ASP.NET MVC version 1 (<%=Html.TextBox("UserName")%>), these

    helper methods utilize the C# 3.5 feature of expressions to generate HTML. Because these

    HTML elements need to be generated to match properties on objects, it’s only fitting that the

    original types and objects are used with expressions to generate the related HTML.

    The Html.LabelFor and Html.TextBoxFor methods used for the UserName property

    in listing 3.10 generate the HTML shown in listing 3.11.

    Ch3_ Palermo _ ASP.NET_toProd <label for="UserName">User name</label> <input id="UserName" name="UserName" type="text" value="" />

Ch3_ Palermo _ ASP.NET_toProd

    HtmlHelper extensions designed for strongly typed views (including those used in For our page to pass accessibility validation, every input element (such as the first line in the preceding code) are listed in table 3.1. listing 3.11) needs to include a corresponding label element (such as the second line).

    Because our label and input elements are generated using expressions, we no longer need to Table 3.1 HTML helpers in ASP.NET MVC 2 worry about hard-coding label and input names.

    The HTML helper Description

    DisplayFor Returns HTML markup for each property in the

    object that’s represented by the expression DisplayTextFor Returns HTML markup for each property in the

    object that’s represented by the specified

    expression

    EditorFor Returns an HTML input element for each property

    in the object that’s represented by the specified

    expression

    CheckBoxFor Returns a check box input element for each

    property in the object that’s represented by the

    specified expression. DropDownListFor Returns an HTML select element for each property

    in the object that’s represented by the specified

    expression using the specified list items HiddenFor Returns an HTML hidden input element for each

    property in the object that’s represented by the

    specified expression LabelFor Returns an HTML label element and the property

    name of the property that’s represented by the

    specified expression ListBoxFor Returns an HTML select element for each property

    in the object that’s represented by the specified

    expression and uses the provided data for the list

    items

    PasswordFor Returns a password input element for each

    property in the object that’s represented by the

    specified expression RadioButtonFor Returns a radio button input element for each Ch3_ Palermo _ ASP.NET_toProd

    Ch3_ Palermo _ ASP.NET_toProd

    TextAreaFor Returns an HTML text area element for each

    property in the object that’s represented by the

    specified expression property in the object that’s represented by the

    specified expression TextBoxFor Returns a text input element for each property in

    the object that’s represented by the specified

    expression ValidateFor Retrieves the validation metadata and validates

    each data field that’s represented by the specified

    expression ValidationMessageFor Returns the HTML markup for a validation-error

    message for each data field that’s represented by

    the specified expression

    Because our form was generated using a strongly typed view, we can take advantage of

    this in the design of our action that the form posts to. Rather than enumerating every input

    field as a separate action method parameter, we can bind all the parameters to the same

    view model we used to render the view, as shown in listing 3.12.

    public ActionResult LogOn(LogOnModel model, string returnUrl) { // Action method body here ... }

    As you can see, our LogOn action method takes a single LogOnModel object, as well as

    the potential return URL, instead of a method parameter for each input element on our form.

    As powerful as the HtmlHelper extensions for strongly typed views can be, we still

    introduce quite a bit of duplication in our views if we rely solely on these extensions for

    generating HTML. For example, if every input element requires a corresponding label, why

    not always include it? Every user interface is different, so the MVC team can’t predict the

    layout everyone wants to use for input and label elements. Instead, we can take advantage

    of a new feature in ASP.NET MVC 2templatesto enforce a standardized approach to

    generating HTML.

    3.5 Using strongly typed templates

    As we move toward using strongly typed views based on a presentation model, we’ll start to see more and more patterns emerge. If a view model object has a Boolean property on a

    form, we’ll almost certainly want to display a check box on a form. Email addresses should

    Ch3_ Palermo _ ASP.NET_toProd

Report this document

For any questions or suggestions please email
cust-service@docsford.com