在jQuery对话框中的MVC中常用的方法

时间:2011-10-13 19:03:22

标签: asp.net asp.net-mvc ajax asp.net-mvc-3

似乎有几种方法可以将jQuery对话框与ASP.NET MVC集成。是否有一种特定的方法成为普遍接受的最佳实践方法?

作为一个例子:我有一个列表页面,点击任何列出的项目的“编辑”在jQuery对话框中打开一个表单,填充项目的详细信息。用户编辑详细信息并单击“保存”。如果保存在服务器端成功,则关闭对话框并使用新数据重新构建列表。如果在服务器端保存失败,则对话框保持打开状态并向用户显示错误消息。

  1. No-JSON方法:每个“编辑”链接都是“编辑”控制器操作的HREF。该控制器操作构建一个与“列表”视图相同的视图,此外它还包括一个部分操作来构建编辑表单,填充它,并定义javascript以将其作为jquery对话框弹出。 “保存”是一个形式的帖子;如果成功,它会将重定向操作返回到列表页面。如果失败,它将重建整个页面(包括在对话框中弹出的表单),同时显示错误消息。
  2. 全JSON方法:列表页面呈现一个空的编辑表单(隐藏),准备弹出一个对话框。 “编辑”链接调用本地javascript,它执行ajax请求以获取完整对象(我定义了一个控制器,它将完整对象作为JsonResult返回)。成功时,它使用对象的数据填充编辑表单并打开对话框。 “save”链接调用本地javascript,它将表单数据捆绑到json对象中,并使用该json对象作为有效负载调用post操作(我定义了一个控制器,它需要该对象,尝试保存,并返回一个指示成功的JsonResult /失败+ errorMessages)。来自ajax请求的成功回调计算返回的对象,并在仍然打开的jquery对话框中显示错误消息,或者关闭对话框并重新加载“list”页面以获取列表中的新数据。
  3. [编辑] Ajax-HTML方法:刚刚看到了this SO discussion,它描述了另一种方法。 “编辑”调用本地javascript,它执行ajax发布以获取对话框的完整HTML(我会编写一个返回部分视图的控制器:完全填充的表单)。它将返回的HTML呈现为jquery对话框,并且还“重新连接”表单提交以执行表单内容的ajax-post(我将编写一个httpPost控制器,与上面的#2相同)。成功回调会评估响应并填充错误消息或关闭对话框。
  4. 我还没有想过其他一些很酷的方法?
  5. 选项1似乎更符合“纯粹的”ASP.NET MVC。但是,它似乎具有大的HTTP有效负载(因为我们在每个请求时将整个页面发送回浏览器),并且服务器端性能较慢(因为我们在每个请求上重新构建列表)。

    选项2似乎与更现代的基于Ajax的Web应用程序(更小的HTTP有效负载,更细粒度的操作)更加一致。但是,似乎许多控制器都是JSON控制器,我将编写大量客户端代码来编组来自JSON对象的数据到/从表单字段,显示错误消息等。它似乎也像我我会错过许多很酷的MVC功能,比如EditorFor()和ValidationMessageFor()。它只是“感觉”就像我正在使用MVC系统而不是它。

    [编辑:添加选项3] 选项3似乎是1和2之间的混合。我使用“纯”MVC方法来构建和填充表单,并返回完整格式的HTML FORM标记。将HTML返回到ajax请求感觉很奇怪,因为它很冗长,但我可以克服它。后期操作很好,紧凑的JSON比ajax“感觉”更好。但是,不幸的是,有效负载对象是FormCollection而不是真正的viewmodel对象。看起来我可以使用一些MVC便利(EditorFor())而不是其他(ValidationMessageFor())。

    我正在寻找“正确”的方式来做到这一点,而不仅仅是以最快的方式将它一起破解。是的,是的,我知道没有普遍的“正确”方式。但我确信有一些明显错误的方法,我想避免它们。

    我在ASP.NET / C#中非常有经验,但我对MVC很新。在此先感谢您的帮助!

    [编辑] - 出色的回复 - 我希望我可以奖励多个答案/赏金,因为我发现有几个回复非常有用。但是,由于我不能,我将最高投票的答案标记为答案。再次感谢所有受访者!

5 个答案:

答案 0 :(得分:12)

我的团队和我在编写支持AJAX的MVC应用程序方面有很多经验,我们使用了所有3种方法。

然而,我最喜欢的是 AJAX-HTML方法 - 使用PartialView呈现对话框的内容,其中可能包括服务器端验证消息和任何其他逻辑。

此方法的最大好处是关注点分离 - 您的视图始终负责呈现您的HTML,并且您的JavaScript不必包含任何文本,呈现JSON所需的标记或“模板”。

另一大优势是所有出色的MVC功能都可用于呈现HTML:强类型视图,HtmlHelperDisplayForEditorFor模板,DataAnnotations,这样可以更容易保持一致,并且可以很好地重构。

请记住,没有必要坚持单一方法。当你的AJAX调用只需要一些简单的东西,比如像“成功”这样的状态更新时,可以使用stringJSON来传达这些消息。需要HTML时使用PartialViews,并在需要通信时使用更简单的方法。

答案 1 :(得分:4)

你的第二种方法, All-JSON方法,似乎越来越普遍使用MVC和MVVM客户端库,如Knockout

在这里你实际上可以拥有JSON中的所有数据(包括列表)和编辑列表项(类似于它们的list item editor demo,只是对话呈现而不是内联,并将数据绑定到只读的跨度在你的单元格中)然后在保存时将整个集合序列化回服务器。或者你可以在每次弹出编辑后用零食保存来完成。

JSFiddle:http://jsfiddle.net/paultyng/weLtH/17/

JS可以清理一下,我没有包含保存按钮,但你应该明白这个想法。编辑对话框也可以是绑定到一个编辑的单个模板,而不是按行进行,这只是使用Knockout执行此操作的最简单方法。

答案 2 :(得分:2)

我认为最好的方法是正常呈现列表。连接编辑链接转到单独的页面(跟我来这里),就像你通常那样。

用JS处理点击链接,然后点击它的href。在编辑操作中,检查Request.IsAjaxRequest(),如果是,则返回部分视图(如果不是),返回完整视图。或者在没有母版页的情况下渲染普通编辑视图(将null传递给View()调用中的母版页参数或调用返回Partial())。获取结果的内容并将其放入对话框中。

还使用JS来处理提交表单并从请求中获取结果。如果未成功,请将视图内容插入对话框以显示存在错误。否则关闭它并继续前进。

这种方法的好处是它非常不引人注目,并且仍然允许那些没有JS的人使用。

答案 3 :(得分:2)

好的,所以你的选择在这里几乎扼杀了渐进增强的概念。 2&如果您的客户端不支持java脚本,则3将无法工作。显然,如果你不在乎,那就好了,但我想我会尝试设计一些东西,以便它们优雅地降级,你在这里要求最佳实践。

所以我构建它的方式,从您的选项1开始。您有编辑按钮,触发另一个加载编辑页面的操作,并且此页面按照正常的mvc设计了所有验证器等。这是你的基本功能所以没有js。

那么下一个问题是我们如何逐步增强这个以获得一个漂亮的弹出窗口而不是一个新的页面?

第一步是创建一个处理程序,在点击时打开附加到编辑链接的对话框(确保e.PreventDefault)。现在为了节省太多的编码工作,我希望重用编辑页面。这需要进行一些重构,因为您不希望包含ajax请求的布局。你可以通过几种方式实现这一目标,但我认为最干净的是将编辑视图的编辑区域作为主编辑视图用于渲染其模型的局部视图。

然后,在您的编辑操作中,您可以检查是否有ajax请求,如果是,则返回PartialView(mypartialeditview)或View(editview),如果没有。

然后,如果您想要轻松生活,那么将结果提交回服务器只需将其视为表单即可。你可以在这里使用micorsoft unobstrive ajax,它会非常简单。您在局部视图中使用Ajax.BeginForm。 n.b.如果ajax不可用,这将降级为正常形式。让这个beginform的AjaxOptions设置为更新对话框中的div,因此如果它以html响应你不需要再做任何事情,那就意味着验证错误。

[小旁边正如你上面提到的那样:就HttpPost处理程序而言,默认的模型模型绑定器非常智能,它可以将表单字段绑定到复杂类对象参数的属性。这也适用于json,因此您不必最终使用许多操作方法来支持不同的场景。]

因此,如果您的更新不成功,帖子处理程序将再次返回部分视图,绑定到模型,这样您就可以获得所有验证器。但是,如果更新成功,我建议不要返回任何内容,操作会重定向到您要重新加载的主页,因为您已经更改了底层。

如果您不喜欢完全重新加载主页面,那么它会变得更加复杂,就像上面的方法一样,您将返回html。您将不得不对此进行jquery以查找隐藏字段或类以指示成功/失败,或者迁移到返回jsonresult的纯json方法。但是,维护/编码方面的情况越来越重。我可能会进行jquery检查并将其连接到ajax.Beginform的完成处理程序。

如果你真的想了解这些东西,我发现Steve Sanderson Pro Asp.net MVC这本书非常宝贵。我最初阅读的是MVC2,但正在阅读MVC3更新。我对这个更新感到好奇,因为它在一些地方被简化了 - 现在更容易理解,但我觉得有些东西不见了,而且当它接近尾声时,它也会像一些错误一样匆忙。我想也许他们现在惊慌失措,因为MVC4正在谈论而且书籍没有出来!虽然它仍然是一本好书,它能很好地涵盖所有这些内容。

希望这会有所帮助。感谢它涵盖了一些与上述答案相同的基础,但希望我为你更多地钻研。

答案 4 :(得分:2)

使用jQuery.tmpl()插件 1

我使用类似的方案,但以不同的方式进行。

我有一个主列表,其中每个项目都包含在一个容器中(无论是表TR还是DIV)。当在简单列表中包含太多数据时,有关该项目的一些常规信息将显示在主列表中。因此,主列表而不是细节(当然)。

每个项目容器通常都是这样写的:

<div class="item" data='<%= item.ToJson() %>'> <!-- single quotes! -->
    item visible list data
</div>

这里的主要部分是我的自定义扩展方法ToJson(),它序列化我的商品数据,可以在客户端轻松使用。

然后我在script标记内包含的列表末尾有一个jQuery模板。此模板是编辑对话框的实际内容,其中所有项目变量都根据需要设置。

每当用户点击项目上的编辑时,我只需通过以下方式解析项目JSON:

// in edit link click handler
var itemData = $.parseJSON($(this).closest("[data]").attr("data"));
// display dialog
displayDialog($("#editDialog").tmpl(itemData));

我的对话框通过Ajax调用处理其余部分,并在调用成功或用户取消编辑时关闭对话框。

这使我的HTML代码保持最小(只有一个模板),而JSON也只包含实际需要的那些属性。

当您的模型类有其他实体引用时该怎么办?

实体之间相互关联是很常见的。假设您有PostComment个实体:

public class Post
{
    public int Id { get; set; }

    public string Body { get; set; }

    public IList<Comment> Comments { get; set; }
}

当然,在服务器上将项目转换为JSON时,您对相关实体不感兴趣。这就是为什么你可以将属性放到相关属性中,这样它们就不会包含在JSON中:

public class Post
{
    public int Id { get; set; }

    public string Body { get; set; }

    [ScriptIgnore]   
    public IList<Comment> Comments { get; set; }
}

这将使JSON序列化程序忽略我们不会在详细信息编辑器中编辑的相关属性。

ToJson()扩展代码

这里放的最后一件事是扩展方法代码,所以在这里:

public static class ObjectExtensions
{
    /// <summary>
    /// Serializes this object instance to JSON string.
    /// </summary>
    /// <param name="instance">Object instance being extended.</param>
    /// <returns>Returns a JSON string that represents current object instance.</returns>
    public static string ToJson(this object instance)
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        // register custom class converters
        serializer.RegisterConverters(...);
        return serializer.Serialize(instance);
    }

    /// <summary>
    /// Serializes this object instance to JSON string.
    /// </summary>
    /// <param name="instance">Object instance being extended.</param>
    /// <param name="recursionDepth">Serialization recursion limit depth.</param>
    /// <returns>Returns a JSON string that represents current object instance.</returns>
    public static string ToJson(this object instance, int recursionDepth)
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        // register custom class converters
        serializer.RegisterConverters(...);
        serializer.RecursionLimit = recursionDepth;
        return serializer.Serialize(instance);
    }
}

当实体过于复杂时该怎么办?

当您的实体不太复杂时,上层方法很有用。在以下情况下:

  • 您的实体很复杂或数据很长(序列化字符串很长)
  • 确定所有项目的JSON对象会非常耗时
  • 主列表显示时的未知JSON对象

然后您可以始终向服务器发送一个远程Ajax调用以获取单个实体并返回

  • 用于对话框模板的JSON对象
  • 使用编辑器返回部分视图

第二种方法更好,因为将对象实例转换为HTML或JSON实际上是类似的任务。但在第二种情况下,不需要客户端模板处理。

但请勿仅因为您而使用远程请求。使用一种更轻松,更好地解决手头问题的方法。

  

1 jQuery template API reference