使用HTML和JavaScript返回PartialView

时间:2022-11-16 07:36:29

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.

我正在进行AJAX调用(使用jQuery)来检索PartialView。与HTML一起,我想发送一个View正在显示的对象的JSON表示。我一直在使用的糟糕方式是将属性作为隐藏输入嵌入到HTML中,这很快变得笨拙并且紧密地耦合太多东西。

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:

我可以在HTML之后的

<%@ 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.

我考虑的另一个选项是对返回JsonResult的动作进行第二次AJAX调用,但这也感觉设计不好。

4 个解决方案

#1


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

我想我找到了一个非常好的方法,只需将JSON包装在HtmlHelper扩展中。这是班级:

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.

我能想到的一个问题是它不是一个完全完美的分离;你仍然在技术上将JavaScript放在HTML中。但是,它不会对服务器进行额外调用,并且IDE中的标记仍然非常干净。

#2


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,

你可以从你的动作方法返回纯粹的json,

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();

#3


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

与安德鲁相同的解决方案,但也许更多的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.

创建一个继承自ViewPage的新类。重写其Render方法以呈现页面本身,然后将其填充到Andrew建议的输出中。因此,所有的黑客攻击都发生在Render方法中,它应该发生在哪里。

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.

我自己没有检查,但猜猜它应该有效。如果你想我可以尝试构建它。

#4


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();

            }

#1


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

我想我找到了一个非常好的方法,只需将JSON包装在HtmlHelper扩展中。这是班级:

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.

我能想到的一个问题是它不是一个完全完美的分离;你仍然在技术上将JavaScript放在HTML中。但是,它不会对服务器进行额外调用,并且IDE中的标记仍然非常干净。

#2


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,

你可以从你的动作方法返回纯粹的json,

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();

#3


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

与安德鲁相同的解决方案,但也许更多的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.

创建一个继承自ViewPage的新类。重写其Render方法以呈现页面本身,然后将其填充到Andrew建议的输出中。因此,所有的黑客攻击都发生在Render方法中,它应该发生在哪里。

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.

我自己没有检查,但猜猜它应该有效。如果你想我可以尝试构建它。

#4


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();

            }