将模型绑定到列表 MVC 4

31

有没有一种将IList项目绑定到视图的模式。我似乎在HttpPost方面遇到了问题。我知道Phil Haack写了一篇不错的文章,但是它已经过时了,他说他们可能会在MVC 4中修复。


1
根据你的表单外观会有所不同。一些视图代码会有所帮助。 - AaronLS
你说你在使用 HttpPost 时遇到了问题。显然这意味着你的代码存在某些特定的问题,能否展示一下?你提到了 Phil Haack 的一篇文章,但没有给出链接。"将项目列表绑定到视图"并不只有一种方法。请不要假定回答你问题的社区会非常慷慨。 - mellamokb
1
这是Phil文章的链接:http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx - Karthik
1
@JT 参考这里的答案:https://dev59.com/ClrUa4cB1Zd3GeqPgx00#7009837 - lahsrah
@JT 我们并没有说你的代码有问题。我们需要看到你的视图模型+视图尝试,才能知道哪种列表绑定技术适用于你的情况。 - AaronLS
显示剩余5条评论
3个回答

58

如果我需要为每个项目显示表单,并针对各种属性提供输入,则我会这样做。但实际上,这取决于我的具体需求。

ViewModel看起来像这样:

public class MyViewModel
{
   public List<Person> Persons{get;set;}
}

视图(当然要用BeginForm):

@model MyViewModel


@for( int i = 0; i < Model.Persons.Count(); ++i)
{
    @Html.HiddenFor(m => m.Persons[i].PersonId)
    @Html.EditorFor(m => m.Persons[i].FirstName) 
    @Html.EditorFor(m => m.Persons[i].LastName)         
}

操作:

[HttpPost]public ViewResult(MyViewModel vm)
{
...

请注意,在后续提交中,只有已输入的属性才会有值。也就是说,如果Person有一个.SSN属性,在表单中没有该字段,那么在后续操作中它将不可用。

请注意,MVC模型绑定的工作方式是仅查找连续的ID。所以像这样有条件地隐藏一个项将导致在第5个项之后不会绑定任何数据,因为一旦遇到ID中断,就会停止绑定。即使有10个人,您也只会在postback时得到前4个人的数据:

@for( int i = 0; i < Model.Persons.Count(); ++i)
{
    if(i != 4)//conditionally hide 5th item, 
    { //but BUG occurs on postback, all items after 5th will not be bound to the the list
      @Html.HiddenFor(m => m.Persons[i].PersonId)
      @Html.EditorFor(m => m.Persons[i].FirstName) 
      @Html.EditorFor(m => m.Persons[i].LastName)           
    }
}

1
我使用的是MVC 5.1,而不是++1,我必须这样做:在@foreach()块内部写成@{ ++i; } - Yustme
@Yustme 或许,如果你有HTML标签,你需要使用@{}将上下文切换回C#代码。但无论如何,这是一个好的提示。 - AaronLS
6
使用foreach声明“i”变量,再使用它进行迭代,相比使用FOR循环来说显得比较丑陋。此外,如果没有必要,请尽量避免使用特定的列表实现。 - Rush Frisby
好的观点,有时我不能使用EditorFor,必须构建<input>,并且喜欢迭代器变量仍然保持不变,因为在所有地方重复索引访问感觉非常不DRY。在这个例子中,person变量没有用处,所以我从foreach改为了for。 - AaronLS
我们可以使用ASP标签助手来完成吗? - akagrawal

11

一个简洁的解决方案是创建一个通用类来处理列表,这样你就不需要每次都创建不同的类。

public class ListModel<T>
{
    public List<T> Items { get; set; }

    public ListModel(List<T> list) {
        Items = list;
    }
}

当您返回视图时,您只需要简单地执行以下操作:

List<customClass> ListOfCustomClass = new List<customClass>();
//Do as needed...
return View(new ListModel<customClass>(ListOfCustomClass));

然后在模型中定义列表:

@model ListModel<customClass>

准备就绪:

@foreach(var element in Model.Items) {
  //do as needed...
}

4

~控制器

namespace ListBindingTest.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {
            List<String> tmp = new List<String>();
            tmp.Add("one");
            tmp.Add("two");
            tmp.Add("Three");
            return View(tmp);
        }

        [HttpPost]
        public ActionResult Send(IList<String> input)
        {
            return View(input);
        }    
    }
}

强类型的索引视图

@model IList<String>

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
    <div>
    @using(Html.BeginForm("Send", "Home", "POST"))
    {
        @Html.EditorFor(x => x)
        <br />
        <input type="submit" value="Send" />
    }
    </div>
</body>
</html>

强类型发送视图

@model IList<String>

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Send</title>
</head>
<body>
    <div>
    @foreach(var element in @Model)
    {
        @element
        <br />
    }
    </div>
</body>
</html>

这是您需要做的一切,将他的MyViewModel模型更改为IList。

Steve,感谢你提供的信息,这很好用。我的问题可能是我没有解释清楚,就是绑定一个动态列表。Sanderson在这篇文章中解决了这个问题 http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/。 - Karthik
@JT 在模型绑定中,无论我在控制器中静态类型列表,还是从数据库或文件中获取它,都没有关系。列表的来源对于模型绑定来说是不相关的。 - Steve's a D
1
我的问题出在HttpPost上。当视图中的对象列表发生变化时,值无法正确地发布。这就是Sanderson解决的问题,我正在使用他的方法。MVC团队知道这个问题,而Sanderson的方法是我见过的最好的。对于没有表达清楚,我向大家道歉。 - Karthik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接