ASP.NET MVC Preview 3: PLaying with ViewPage


Note: This entry may ramble a bit, as it is being written as I experiment.
 
I have now been in the waters long enough to be baptized and wanted to play around a bit while I blog. This first post is based on an issue I noticed in Preview 2 with the MVC Membership Starter Kit (blogged in my last entry) and how I played around with it.
 
Suppose you wish to include some JavaScript in your page and feed it from view data. Let’s do something really simple that is potentially error prone. We are going to create a simple form with a button and we are going to use ViewData. Simple enough, eh? To make this simple, let’s just whack the default set up. After all this is just a test, right? In this, we will create a JavaScript that displays a message.
 
So, create a new MVC project from the template. Then, open the controller and put the following in the index() action result method:
 
public ActionResult Index()
{
    ViewData[
"Title"] = "Home Page";
    ViewData[
"Message"] = "Welcome to ASP.NET MVC!";
   
//Add this line
    ViewData["AlertMessage"] = "This is a message in a popup";

    return View();
}

 
Now, move to the view and place the JavaScript bits in the View.
 
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
    <h2><%= Html.Encode(ViewData["Message"]) %></h2>
    <p>
        To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc&quot; title="ASP.NET MVC Website">http://asp.net/mvc</a>.
   
</p>
    <script>
    /* <![CDATA[ */
    alert(‘<%= ViewData["AlertMessage"] %>’);
   
/* ]]> */
    </script>

</
asp:Content>
 
The portion highlighted is all that I added to the mix. If we run the page now, we have a popup with an alert message. The next step is to add a button to the form, so we can pretend this is a form. So, I add the following:
 
    <% using(Html.Form("Home", "SimpleSubmit"))
       {
%>
      
       <%
=Html.SubmitButton("submit","Submit", null) %>
      
    <% } %>
 
While this is not a test, let’s run a red … green … refactor exercise anyway, even if it is backwards (should have tested the controller first). I open the page again and there is a button. Clicking the button produces an error:
 

Server Error in ‘/’ Application.


The resource cannot be found.

Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable.  Please review the following URL and make sure that it is spelled correctly.

Requested URL: /Home/SimpleSubmit


Version Information: Microsoft .NET Framework Version:2.0.50727.1434; ASP.NET Version:2.0.50727.1434

 
This is just what I expected! Smile Now, we will implement this in the controller as a method called SimpleSubmit() – yes, the name sucks, but this is an inane example to illustrate a point.
 
        public ActionResult SimpleSubmit()
        {
            ViewData[
"Title"] = "Home Page Submitted";
            ViewData[
"Message"] = "You just clicked my button!";

            return View("Index");
        }

 
Run the app again and click the submit button and what happens? Empty message in the popup. This is a bit nicer than preview 2, which would error out.
 
Oh, since this is an empty popup, I am going to assume we only want a popup when there is a message. One way to do this is to check if the ViewData contains this key.
 
    <% if(ViewData.ContainsKey("AlertMessage"))
       {
%>
   
<script>
    /* <![CDATA[ */
        alert(‘<%= ViewData["AlertMessage"] %>’);
   
/* ]]> */
    </script>
    <% } %>

This at least keeps things a bit sane, but it is not completely to my liking. How about if I control the class that feeds the page? First, we code a simple class:

public class TestClass
{
   
public string Title { get; set; }
   
public string Message { get; set; }
   
public string AlertMessage { get; set; }

    public TestClass(string title, string message, string alertMessage)
    {
        Title = title;
        Message = message;
        AlertMessage = alertMessage;
    }
}

 
Now, this does not buy me much, but it does allow me to simplify my Index() method, which makes it a decent enough refactor. The Index method now looks like this:
 
    public ActionResult Test()
    {
       
TestClass test = new TestClass("Home Page"
            , "Welcome to ASP.NET MVC!"
            , "You just clicked my button");
       
return View("Test", test);
    }
 
To make this actually work, I have one other small step. I have to change the Index declaration to take the TestClass.
 
    public partial class Index : ViewPage<TestClass>
    {
    }
 
There is another syntax you can use here if you want to avoid having an empty code behind file, which is detailed on Troy Goode’s blog. The basic syntax is:
 
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage`1[ [MyCompany.MyProject.TestClass,MyCompany.MyProject] ]" %>
 
In this, you are stating the name of the class followed by the name of the assembly it is contained in. As I mentioned, Troy has further details in this post.
 
Now, the first thought may be "you need to change the code in the page", but that is not correct. When you make a simple class like this, it can pull directly from the elements using the syntax we are already using. To prove this, let’s change the page, but avoid the Master Page, which contains the following line:
 
<title><%= Html.Encode(ViewData["Title"]) %></title>
 
The page will be changed to the following:
 
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
AutoEventWireup="true" CodeBehind="Test.aspx.cs" Inherits="TestMVC.Views.Home.Test" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h2><%= ViewData.Model.Message %></h2>
    <p>
        To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc&quot; title="ASP.NET MVC Website">http://asp.net/mvc</a>.
   
</p>
    <% using(Html.Form("Home", "SecondSimpleSubmit"))
       {
%>
      
       <%
=Html.SubmitButton("submit","Submit", null) %>
      
    <% }
%>
    <%
if(ViewData.Model.AlertMessage != String.Empty)
       {
%>
   
<script>
    /* <![CDATA[ */
        alert(‘<%= ViewData.Model.AlertMessage %>’);
   
/* ]]> */
    </script>
    <% } %>
</asp:Content>
 
Run the page and it works fine, so we can use one of two syntaxes when we are using ViewPage<T> with a class with simple properties:
 
alert(‘<%= ViewData["AlertMessage"] %>’);
 
or
 
alert(‘<%= ViewData.Model.AlertMessage %>’);
 
NOTE: If you are using a build prior to Preview 3 for some reason, you will have to use ViewData.Property instead of ViewData.Model.Property.
 
Now, this really does not provide me much at this point in time, but it does give me the potential to simplify rather large JavaScript sections (something more than an alert, by adding a public property for AlertJavaScriptBlock (I will work on the names later), which allows me to do something like:
 
    <% if(!ViewData.Model.IsPostBack){ %>
       <%
=ViewData.Model.AlertJavaScriptBlock %>
    <% } %>
 
There has to be a way to write this even more tersely, but I will have to figure out what <%= %> corresponds to in code (something similar to Response.Write?). If you know, just ping me, as I would love to keep the syntax as tight as possible. Regardless, "injecting" the entire JavaScript block when needed, I avoid a lot of code in the view. This is important to me as I already have components outputting a large amount of client side JavaScript (a MapQuest API component primarily).
 
One problem solved. I now have to go to themes and dink a bit further with Membership. I am sure I will blog more about these adventures.
 
Peace and Grace,
Greg
Advertisements

4 Responses to ASP.NET MVC Preview 3: PLaying with ViewPage

  1. Troy says:

    Greg, I’m fairly certain that <%="blah" %> equates EXACTLY to <% Response.Write("blah"); %>.At least I know it did in WebForms, and from what I’ve deduced from the MVC framework’s WebFormsViewEngine (http://www.codeplex.com/aspnet/SourceControl/FileView.aspx?itemId=8309&changeSetId=7061) there shouldn’t be a difference.You could always try precompiling the app and then running the assembly through Reflector to verify that though.

  2. Gregory says:

    I am suspecting the same, although using Response.Write in code behind puts all of the response bits at the top. I will try:
     
       <% if(!ViewData.Model.IsPostBack){ %>       Response.Write(ViewData.Model.AlertJavaScriptBlock)
    } %>
     
    and see where I am after all of that. Thanks for the post.
     
    Peace and Grace,Greg

  3. Troy says:

    Strange, I’m not sure why my name is displaying as "(no name)"…Response.Write puts whatever you are outputting at the top of the response stream because (assuming you put it in Page_Load or Page_PreRender) it occurs before the page renders the markup in the ASPX. If you use <% Response.Write( "etc" ); %> in the ASPX page rather than code-behind it will render correctly.Everything in an ASPX page is really passed through Respone.Write. If I have an ASPX page that looks like:<h1>Hello</h1><h2><% ="World" %></h2>That is converted into:Response.Write( "<h1>Hello</h1>\n<h2>" );Response.Write( "World" );Response.Write( "</h2>" );… when the page is compiled into a class (which inherits from the code-behind class).

  4. Gregory says:

    Thanks Troy, that is very useful.
     
    Now to figure out whether or not I have to dink with the MVC source to get the ASP.NET controls to work properly. I am having fun playing with the MVC bits now that they are in Preview 3.
     
    Peace and Grace,Greg

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: