如何让后动作方法将原始viewmodel的子对象作为参数? [英] How to let the post action method take a subobject of the original viewmodel as a parameter?

查看:88
本文介绍了如何让后动作方法将原始viewmodel的子对象作为参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的模特

public class IndexViewModel
{
    public FilterConditions conditions { get; set }
    public IEnumerable<SelectListItem> Countries { get; set }
}

public class FilterConditions
{
    public string condition11 { get; set }
    // ...
}

我有一个像这样的Index动作方法:

public ActionResult Index()
{
    var model = new IndexViewModel();

    // fill the model here with default values

    return View(model);
}

该视图使用过滤条件作为输入类型来呈现表单.

现在,我希望通过该操作方法处理从该表格发回的邮件:

[HttpPost]
public ActionResult Index(FilterConditions model)
{
    // do some magic with model and return another view here
}

这实际上是有效的(我在方法中放置了一个断点,它被调用了),但是我模型的属性始终为空(默认值),而它们应该包含由表单发布的值./p>

当我像这样修改操作方法时:

[HttpPost]
public ActionResult Index(IndexViewModel model)
{
    // do some magic with model.conditions and return another view here
}

一切正常,但是这不是正确的"(IMHO),因为返回时我不需要国家"列表,所以我只需要选定的国家(这是条件之一).

有什么好的方法(最佳实践)来完成这项工作,而不必将整个原始viewmodel作为输入参数?

顺便说一句,我使用的是ASP.NET MVC 2(我认为这并不重要,因为我认为这与MVC 3中的问题相同,但我对此并不完全确定).

(我一直在Internet上寻找有关"asp.net mvc"中的下拉列表和视图模型的最佳实践",但是我发现的不同建议并没有真正相互吻合,而且很多建议也已经过时了我没有找到关于这方面的官方"最佳实践,我希望我朝着正确的方向发展(将列表作为我的视图模型的一部分),如果没有,请随时对我进行纠正.如果您知道的话,也可以随时向我指出认可的最佳做法".)

更新:

我发现可以将[Bind]属性与"filterconditions"前缀一起使用.这确实适用于这种观点.但是我的原始问题(我承认,它没有包含在我的问题中)没有得到解决.

碰巧也从另一个没有前缀的视图(是ajax调用)调用了这个特定的action方法,在这种情况下,它现在不再起作用.有什么建议吗?

解决方案

我找到了解决方案.

显然,当我使用与类型名称相同的名称作为参数变量时(大小写不必匹配),如下所示:

[HttpPost]
public ActionResult Index(FilterConditions filterConditions)
{
    // do some magic with model and return another view here
    // now the filterConditions variable actually contains values!
}

一切工作均应正常进行(我的filterConditions的值不再为空/空).显然,默认的modelbinder使用参数名称作为绑定的潜在前缀.

我很高兴发现了它,但是如果在某个地方更清楚地记录下来,那就太好了.一点都不明显.

修改: 根据要求:这是我视图(aspx)中的代码:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyProject.Models.IndexViewModel>" %>

<%-- ... more stuff here ... --%>

<% using (Html.BeginForm())
   {%>
    <%= Html.ValidationSummary(true)%>

    <fieldset>
        <div class="editor-label">
            <%= Html.LabelFor(model => model.FilterConditions.Country)%>
        </div>
        <div class="editor-field">
            <%= Html.DropDownListFor(model => model.FilterConditions.Country, Model.Countries)%>
            <%= Html.ValidationMessageFor(model => model.FilterConditions.Country)%>
        </div>

        <div class="editor-label">
            <%= Html.LabelFor(model => model.FilterConditions.Make)%>
        </div>
        <div class="editor-field">
            <%= Html.TextBoxFor(model => model.FilterConditions.Make)%>
            <%= Html.ValidationMessageFor(model => model.FilterConditions.Make)%>
        </div>

        <%-- ... more fields inserted here ... --%>

        <p>
            <input type="submit" value="  Search...  " />
        </p>
    </fieldset>

<% } %>

This is my model:

public class IndexViewModel
{
    public FilterConditions conditions { get; set }
    public IEnumerable<SelectListItem> Countries { get; set }
}

public class FilterConditions
{
    public string condition11 { get; set }
    // ...
}

And I have an Index action method like so:

public ActionResult Index()
{
    var model = new IndexViewModel();

    // fill the model here with default values

    return View(model);
}

The view renders a form with the filterconditions as input types.

Now I want the post back from that form be handled by this action method:

[HttpPost]
public ActionResult Index(FilterConditions model)
{
    // do some magic with model and return another view here
}

and this actually works (I put a breakpoint in the method, and it gets called), but the properties of my model are always empty (default values), while they should contain the values which were posted by the form.

When I modify the action method like this:

[HttpPost]
public ActionResult Index(IndexViewModel model)
{
    // do some magic with model.conditions and return another view here
}

It all works like it should, but this is not "right" (IMHO), as I don't need the ´Countries´ list on return, I only need the selected country (which is one of the conditions).

What is a nice (best practice) way to make this work without having to take the whole original viewmodel as an input parameter?

Btw, I'm using ASP.NET MVC 2 (I don't think it really matters, as I think it's the same problem in MVC 3, but I'm not entirely sure of that).

(I have been looking around the internet for "best practices" regarding dropdownlists and viewmodels within asp.net mvc, but the different recommendations I found didn't really line up with each other, and a lot is already outdated as well. I didn't find an "official" best practice around this. I hope I'm going in the right direction (having the list as part of my viewmodel), feel free to correct me on this matter if I'm not. Also feel free to point me to "endorsed best practices" about this if you know of any.)

Update:

I found out that I can use the [Bind] attribute with a Prefix of "filterconditions". And this indeed works for this view. But my original problem (I admit, it was not included in my question) is not solved.

It happens that this particular action method is also called from another view (it is an ajax call) where it doesn't have that prefix, in that case it doesn't work any more now. Any suggestions?

解决方案

I've found the solution.

Apparently, when I use the same name for the parameter variable as the name of the type (the case doesn't have to match), like this:

[HttpPost]
public ActionResult Index(FilterConditions filterConditions)
{
    // do some magic with model and return another view here
    // now the filterConditions variable actually contains values!
}

Everything works like it should (the values of my filterConditions are not empty/null anymore). Apparently, the default modelbinder uses the name of the parameter as the potential prefix for the binding.

I'm glad I found out, but it would be nice if this is more clearly documented somewhere. It's not obvious at all.

Edit: On request: this is the code in my view (aspx):

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyProject.Models.IndexViewModel>" %>

<%-- ... more stuff here ... --%>

<% using (Html.BeginForm())
   {%>
    <%= Html.ValidationSummary(true)%>

    <fieldset>
        <div class="editor-label">
            <%= Html.LabelFor(model => model.FilterConditions.Country)%>
        </div>
        <div class="editor-field">
            <%= Html.DropDownListFor(model => model.FilterConditions.Country, Model.Countries)%>
            <%= Html.ValidationMessageFor(model => model.FilterConditions.Country)%>
        </div>

        <div class="editor-label">
            <%= Html.LabelFor(model => model.FilterConditions.Make)%>
        </div>
        <div class="editor-field">
            <%= Html.TextBoxFor(model => model.FilterConditions.Make)%>
            <%= Html.ValidationMessageFor(model => model.FilterConditions.Make)%>
        </div>

        <%-- ... more fields inserted here ... --%>

        <p>
            <input type="submit" value="  Search...  " />
        </p>
    </fieldset>

<% } %>

这篇关于如何让后动作方法将原始viewmodel的子对象作为参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆