从ASP.NET MVC Action返回部分视图和JSON
我正在将KnockoutJS引入现有应用程序。 我的计划是修改/利用我们已经创建的现有部分视图,并使用Knockout的声明属性将它们绑定到JS视图模型。 当我对一个动作进行AJAX调用时,理想情况下我希望该动作返回局部视图的HTML和JSON对象。 然后我可以用HTML填充div,将JSON转换为Knockout对象并将其绑定到HTML。 但我无法弄清楚如何从行动中返回两者。
我需要完整的视图模型,因为我将更新它并最终将其发送回服务器。
我想过让动作返回局部视图(已绑定到模型),并在局部视图中,包含javascript以将.Net模型转换为Knockout对象。 但是我觉得像这样分散JS是混乱而且不可维护的。 我宁愿让一切都接近原来的ajax调用。
我想另一个选择是进行两次动作调用。 一个用于JSON,另一个用于局部视图。 但必须有一个更光滑的方式。
关于如何最好地做到这一点的任何想法?
我确信有很多方法可以做到这一点。 我从控制器手动渲染视图,然后将渲染的视图作为我的JSON响应的一部分传回。
这保留了每个实体的责任。 视图仍然使用视图引擎定位,可以重复使用。 除了名称和模型类型之外,控制器对视图知之甚少或根本不知道。
手动渲染
public static class RenderHelper { public static string PartialView( Controller controller, string viewName, object model ) { controller.ViewData.Model = model; using( var sw = new StringWriter() ) { var viewResult = ViewEngines.Engines.FindPartialView( controller.ControllerContext, viewName ); var viewContext = new ViewContext( controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw ); viewResult.View.Render( viewContext, sw ); viewResult.ViewEngine.ReleaseView( controller.ControllerContext, viewResult.View ); return sw.ToString(); } } }
在你的行动方法中:
object model = null; // whatever you want var obj = new { someOtherProperty = "hello", view = RenderHelper.PartialView( this, "_PartialName", model ) }; return Json( obj );
请注意,我正在返回一个匿名类型。 您可以返回所需的任何(可序列化)类型,只要它具有渲染视图的字符串属性即可。
测试
测试使用手动渲染的操作需要稍作修改。 这是因为渲染视图比在MVC管道中渲染时要早一些。
手动渲染
- 输入操作方法
- 显式渲染视图< - 这将使测试调用操作变得困难
- 退出动作方法
自动渲染
- 输入操作方法
- 创建视图结果
- 退出动作方法
- 处理视图结果(从而渲染视图)
换句话说,我们的手动渲染过程启动了许多其他难以测试的操作(例如与构建管理器交互以编译视图)。
假设您希望测试操作方法而不是视图的实际内容,则可以检查代码是否在托管环境中执行。
public static string PartialView( Controller controller, string viewName, object model ) { // returns false from a VS 2013 unit test, true from IIS if( !HostingEnvironment.IsHosted ) { // return whatever you want here return string.Empty; } // continue as usual }
检查HostingEnvironment.IsHosted
是便宜的(在引擎盖下,它只是一个空检查)。
您可以在partial上创建一个隐藏的 ,其值设置为ViewModel的JSON字符串。 然后在渲染局部视图之前,从该字段中获取JSON值,然后对其进行解析。 然后将其从局部视图中删除,将其插入到页面中,然后执行
ko.applyBindingsToDescendants(viewModel, $("#parentElement")[0])
我不完全确定我对这种方法的看法,这只是一种理论。 我没有测试过这个,但我怀疑它会起作用。 您需要注意的一个引导陷阱是浏览器尝试缓存您的GET请求。 在您的ajax请求中,您需要执行以下操作:
$.ajax({ url: "/", type: 'GET', cache: 'false' });
或者只是做一个$.post
请求。 ( 参考 )
所以这是一个选择。