ASP.NET MVC 中的可重用组件
我的网站上有一些功能(一些 UI 和相关功能),我希望能够在多个页面上重复使用。出于这个问题的目的,我们假设这是一个“评论”功能。
我的组件应用程序中有一个区域,该区域内有一个控制器:/Controllers/CommentController
和两个部分视图:/Views/Comment/Index .ascx
(用于列出评论)和/Views/Comment/Create.ascx
(用于创建评论)。
CommentController 看起来像这样:
public class CommentController : Controller
{
[ChildActionOnly]
public ActionResult Index()
{
return PartialView(GetComments());
}
[HttpGet]
[ChildActionOnly]
public ActionResult Create()
{
return PartialView(); //this is wrong.
}
[HttpPost]
[ChildActionOnly]
public ActionResult Create(FormCollection formValues)
{
SaveComment(formValues);
return RedirectToAction("Index"); //this is wrong too.
}
}
索引部分视图:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<div>
<% foreach (var item in Model) { %>
<div>
<%: item.Comment %>
</div>
<% } %>
<%: Html.ActionLink("Add a Comment", "Create", "Comment", new { area = "Components" }, null) %>
</div>
创建部分视图:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<div>
<% using (Html.BeginForm())
{%>
Enter your comment:
<div>
<input type="text" name="comment" />
</div>
<p>
<input type="submit" value="Create" />
<% //also render a cancel button and redirect to "Index" view %>
</p>
<% } %>
</div>
索引部分视图包含在带有 RenderAction 的视图中,如下所示:
<% Html.RenderAction("Index", "Comment", new { area = "Components" }); %>
此代码不起作用,因为部分视图中的表单提交到 CommentsController 上标记为 [ChildActionOnly]
的操作(这是设计使然,我不希望独立于托管页面请求“组件”)。
如何使这种“组件”方法发挥作用,即拥有一个部分视图,可以提交表单来更改页面内组件的状态,而不会丢失托管页面本身?
编辑: 澄清一下,使用 [ChildActionOnly]
不是我的问题。如果我从操作方法中删除该属性,我的代码仅“有效”,因为它不会引发异常。当提交表单时,我的“组件”仍然会脱离其托管页面(因为我告诉表单提交到部分视图的 URL!)。
I have feature on my website (some UI and associated functionality) that I want to be able to reuse on multiple pages. For the purposes of this question, let's say it's a "Comments" feature.
There is an area in my application for Components and within the area are a controller: /Controllers/CommentController
, and two partial views: /Views/Comment/Index.ascx
(for listing comments) and /Views/Comment/Create.ascx
(for creating comments).
CommentController looks something like this:
public class CommentController : Controller
{
[ChildActionOnly]
public ActionResult Index()
{
return PartialView(GetComments());
}
[HttpGet]
[ChildActionOnly]
public ActionResult Create()
{
return PartialView(); //this is wrong.
}
[HttpPost]
[ChildActionOnly]
public ActionResult Create(FormCollection formValues)
{
SaveComment(formValues);
return RedirectToAction("Index"); //this is wrong too.
}
}
Index Partial View:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<div>
<% foreach (var item in Model) { %>
<div>
<%: item.Comment %>
</div>
<% } %>
<%: Html.ActionLink("Add a Comment", "Create", "Comment", new { area = "Components" }, null) %>
</div>
Create Partial View:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<dynamic>" %>
<div>
<% using (Html.BeginForm())
{%>
Enter your comment:
<div>
<input type="text" name="comment" />
</div>
<p>
<input type="submit" value="Create" />
<% //also render a cancel button and redirect to "Index" view %>
</p>
<% } %>
</div>
The Index partial view is included in a view with RenderAction
, like so:
<% Html.RenderAction("Index", "Comment", new { area = "Components" }); %>
This code doesn't work because the forms within the partial views submit to actions on the CommentsController that are marked [ChildActionOnly]
(this is by design, I don't want the "Components" to be requested independently of a hosting page).
How can I make this "component" approach work, i.e. have a partial view that submits a form to change the state of a component within a page without losing the hosting page itself?
EDIT:
To clarify, the use of [ChildActionOnly]
is not my problem here. If I remove the attribute from my action methods, my code only "works" in that it doesn't throw an exception. My "component" still breaks out of its hosting page when its form is submitted (because I'm telling the form to submit to the partial view's URL!).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
通过要求表单定位标记为 ChildActionOnly 的操作,您正在使 MVC 与自己作斗争。
当我设计一个高度可重用的向导框架时,我对这个问题的解决方案是不将操作标记为 ChildActionOnly,而是检测请求是 ajax 请求还是只是普通请求。
所有这些的代码都被打包到一个基本控制器类中。在派生控制器中,您可以执行以下操作:
基本控制器的 Navigate() 方法决定是返回完整视图还是仅返回部分视图,具体取决于它是否是 ajax 请求。这样,您就永远无法单独返回部分视图。
为了确定它是否是 Ajax 请求,我结合使用了 Request.IsAjaxRequest() 和 TempData。需要 TempData 是因为我的向导框架实现了开箱即用的 PRG 模式,因此我需要坚持原始帖子是 ajax 帖子这一事实。
我想这只是一个解决方案,需要经过一些尝试和错误才能得到正确的解决方案。但现在我在培养像 JK 罗琳那样的巫师后过着幸福的生活......
You are making MVC fight itself by asking a form to target an action that is marked as ChildActionOnly.
My solution to this problem when I was designing a highly reusable wizard framework, was to NOT mark the actions as ChildActionOnly but instead to detect if the request was an ajax one or just a plain vanilla request.
The code for all this is packaged into a base controller class. In your derived controllers, you do something like:
Where the Navigate() method of the base controller has decided whether to return the full view or just the partial view, depending on whether it is, or isn't, an ajax request. That way, you can never return the partial view in isolation.
To ascertain if it is an Ajax request, I used a combination of Request.IsAjaxRequest() and TempData. The TempData is needed because my wizard framework implements the PRG pattern out of the box, so I need to persist the fact that the original post was an ajax one.
I guess this is just one solution and it took a bit of trial and error to get it right. But now I live happily ever after developing wizards like I was JK Rowling...
使用 Ajax 发布部分内容。
Use Ajax to post the partial.