tags:

views:

538

answers:

1

I have a bit of a problem with user controls. Basically what I want to accomplish is the following:

  1. I have a view for editing an invoice.
  2. In this view I have a usercontrol with a list of invoice items
  3. I also have a div that is activated with jQuery for adding a new invoice item
  4. When I add the invoice item I want to refresh just the user control with the list of items

How would I do this without hacks? Something I was thinking of was the following:

[AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken]
public ActionResult Create(InvoiceLine line)
{
if (Request.IsAjaxRequest())
{
 if (!ModelState.IsValid)
 {
  return PartialView("CreateLineControl", product);
 }
}   
return PartialView("DisplayLinesControl", product);
}
A: 

First you would call an aspx page w/ jQuery (we will use an http handler that we will map in the web.config below, more on this later)

the basic idea is that we want the server side to render the user control as xhtml and dump this "updated" markup back into the DOM in our success method (client side)

$.ajax({
    type: "GET",
    url: "UserDetails.aspx?id=" + id,
    dataType: "html",
    error: function(XMLHttpRequest, textStatus, errorThrown)
    {
        alert(XMLHttpRequest.responseText);
    },
    success: function(xhtml)
    {
        var container = document.createElement('div');

        container.innerHTML = xhtml;

        document.getElementById('someElement').appendChild(container);
    }
});

The technique below is what I used to leverage a user control via the HttpHandler to reuse the control for both ajax and .net work

The below was done w/ .NET 1.1 (but i'm sure you can do it in .NET 2.0+) the class below implements IHttpHandler, and the real work is in the process request sub as you can see below

The only issue I had with this at the time was that asp.net controls would not render w/out a form tag in the user control so I used normal html and all was good

Public Class AJAXHandler

    Implements IHttpHandler

    Public ReadOnly Property IsReusable() As Boolean Implements System.Web.IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

    Public Sub ProcessRequest(ByVal context As System.Web.HttpContext) Implements System.Web.IHttpHandler.ProcessRequest
        Dim Request As System.Web.HttpRequest = context.Request
        Dim path As String = Request.ApplicationPath
        Dim newHtml As String
        Dim pg As New System.Web.UI.Page

        context.Response.Cache.SetCacheability(HttpCacheability.NoCache)
        context.Response.ContentType = "text/html"

                                Dim uc As New UserDetail
                                uc = CType(pg.UserControl(path + "/Controls/UserDetail.ascx"), UserDetail)
                                Dim sb As New System.Text.StringBuilder
                                Dim sw As New System.IO.StringWriter(sb)
                                Dim htmlTW As New HtmlTextWriter(sw)

                                uc.LoadPage(custid, CType(pro, Integer))
                                uc.RenderControl(htmlTW)
                                context.Response.Write(sb.ToString())
                                context.Response.End()

    End Sub


End Class

And finally in your web.config you need to define the handler and map it to the aspx path you listed in your ajax call

  <system.web>
    <httpHandlers>
     <add verb="*" path="UserDetails.aspx" type="project.AJAXHandler, project" />
    </httpHandlers>
  </system.web>

Now you can call the user control with UserDetails.aspx and render the user control as html. Then after you render this it will return html (after response.end is called)

then in javascript you can find the parent DOM element to your user control, remove it and append or innerHTML this new html

Update

Above is the solution I used with webforms, but with MVC the below will produce the same result with much less work.

The jQuery function would be the same but on the server side you would simply create a new controller action + PartialView w/ the markup you wanted (basically a user control)

Function Edit(ByVal id As Integer) As ActionResult
    Dim User As User = UserService.GetUserById(id)

    Return PartialView("User", User)
End Function

Now inside my ascx I simply render the html and this is what gets sent back to the browser for the container.innerHTML work (again the client side code is the same for both MVC and Webforms in this scenario)

<%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl(Of User)" %>
<% Dim User As User = DirectCast(Model, User)%>
<div id="innerDetail">
    <label for='username'>Username</label>
    <input type="text" id='username' name='username' value="<%= User.Username %>" /><br />

    <a id='lnkUpdate' href='/Application/User.aspx/Edit/<%= User.ID %>' onclick='UpdateUser(this); return false;'>Update User Information</a>
    <span id='lblUpdateStatus' style='display: inline;'></span>
    </div>
</div>

The reason this works with much less code in MVC is that we don't have to work around the page lifecycle that is required with a normal aspx file in webforms.

Toran Billups
It's a very nice solution. I have not had time to play around with it yet. Will get back to you on this. Time just flies :)
mhenrixon
I believe that you don't need a handler for this because controller methods are public and accessible via proper URLs. But that's a right direction. :)
Arnis L.
That will have to do :)
mhenrixon