3

I am making an AJAX call (with jQuery) to retrieve a PartialView. Along with the HTML I'd like to send back a JSON representation of the object the View is displaying. The crappy way that I've been using is embedding properties as hidden inputs in the HTML, which quickly becomes unwieldy and tightly couples far too much stuff.

I could just send the JavaScript in a <script> tag after the HTML, but I'm really anal about keeping those things separate. That would look like this:

<%@ Control Language="C#" AutoEventWireup="true" Inherits="System.Web.Mvc.ViewUserControl<Person>" %>
<div class="person">
  First Name: <%= Html.TextBox("FirstName", Model.FirstName) %>
  Last Name: <%= Html.TextBox("LastName", Model.LastName) %>
</div>
<script type="text/javascript">
  // JsonSerialized object goes here
</script>

Another option I considered is to make a second AJAX call to an action that returns JsonResult, but that also feels like bad design.

3
  • What did you go with in the end? I have the same issue now. Commented May 26, 2009 at 10:27
  • @swilliams, could you post the jQuery code that is retrieving the partial view. I am interested in seeing how it is done. Thanks Commented Aug 27, 2009 at 16:16
  • @Picflight - Same as you would a normal View and the $.post() or $.ajax() functions. The trick is to just return a PartialView in the Action. It's the same from the perspective of jQuery, an HTML response. My "real" solution is down below. Commented Aug 27, 2009 at 21:20

4 Answers 4

2

I think I found a pretty good way to do this, just wrap the JSON up in an HtmlHelper extension. Here's the class:

using System.Web.Script.Serialization;

public static class JsonExtensions {
    public static string Json(this HtmlHelper html, string variableName) {
        return Json(html, variableName, html.ViewData.Model);
    }

    public static string Json(this HtmlHelper html, string variableName, object model) {
        TagBuilder tag = new TagBuilder("script");
        tag.Attributes.Add("type", "text/javascript");
        JavaScriptSerializer jsonSerializer = new JavaScriptSerializer();
        tag.InnerHtml = "var " + variableName + " = " + jsonSerializer.Serialize(model) + ";";
        return tag.ToString();
    }
}

And you call it via:

<%= Html.Json("foo") %>
<%= Html.Json("bar", Model.Something) %>

The one catch that I can think of is that it isn't a completely perfect separation; you are still technically putting JavaScript in the HTML. But, it doesn't make an extra call to the server, and the markup in the IDE is still very clean.

Sign up to request clarification or add additional context in comments.

Comments

1

Probably not the most elegant answer you'll get, but just to throw this out there:

You could return purely json from your action method,

something that would look like this:

{
    Html: "<div class=\"person\"> etc...",
    Json: { // your object here
          }
}

in your controller you'll need something like this to render your view:

var existingContext = HttpContext.Current;
var writer = new StringWriter();
var response = new HttpResponse(writer);
var httpContext = new HttpContext(existingContext.Request, response);

var viewResult = myAction(bla);

HttpContext.Current = httpContext;

viewResult.ExecuteResult(this.ControllerContext)

HttpContext.Current = existingContext;
var viewAsHtml = writer.ToString();

4 Comments

Interesting. I'll take a look at that, but I don't know if that "feels" right either, almost like short-circuiting the MVC framework. I'm probably just being picky though :).
i know, smells a bit hacky doesnt it? Why dont you just return your person data in the json too, and build the html with javascript?
There are some things in the view that are much easier to do with the MVC templating.
Agreed! I have the same issue and building the HTML via jQuery would be tricky
1

The same solution as Andrew, but maybe a bit more MVC'ish...

Create a new class which inherits from ViewPage. Override its Render method to render the page itself, and then stuff that into the output Andrew suggested. So that all the hacking occurs in the Render method, where it should happen.

Now each view that you create you change the line:

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<MyModel>" %>

with

<%@ Page Title="" Language="C#" Inherits="CustomViewPage<MyModel>" %>

I didn't check it myself, but guess it should work. If you want I can try and build it.

Comments

0
public static string RenderPartialToString(string controlName, object viewData, object model, System.Web.Routing.RequestContext viewContext)
            {

                ViewDataDictionary vd = new ViewDataDictionary(viewData);
                ViewPage vp = new ViewPage { ViewData = vd };

                vp.ViewData = vd;
                vp.ViewData.Model = model;
                vp.ViewContext = new ViewContext();
                vp.Url = new UrlHelper(viewContext);

                Control control = vp.LoadControl(controlName);

                vp.Controls.Add(control);

                StringBuilder sb = new StringBuilder();

                using (StringWriter sw = new StringWriter(sb))
                {

                    using (HtmlTextWriter tw = new HtmlTextWriter(sw))
                    {

                        vp.RenderControl(tw);

                    }

                }

                return sb.ToString();

            }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.