2014-01-21 29 views
0

我有一些功能需要放在多个页面上,所以我将它作为部分视图实现。在表单发布期间绑定复杂对象

在这个部分视图中,我有一个下拉菜单,一个添加按钮和一个“项目”,每个项目都包含一个删除按钮。功能很明显。点击任何删除按钮删除相关项目,从下拉列表中选择一个项目,然后点击添加添加该项目。

复杂的是,这需要完全在JavaScript中发生 - 对项目列表的更改需要完全发生在客户端,并且在整个表单被提交之前服务器上不会发生任何事情。 (也就是说,我们不希望在每次更改时通过ajax更新服务器,我们希望收集更改并在提交表单时全部提交它们。)

第二个复杂性是该表单需要很漂亮大大减少,对父视图的要求尽可能少。

所以我创建了一个视图模型的部分:

public class ItemsModel 
{ 
    // The list to be displayed in the dropdown 
    public List<KeyValuePair<string, string>> itemsList { get; set; } 
    // The list of selected items 
    public List<string> items { get; set; } 
    public string itemsJson 
    { 
     get { return JsonConvert.SerializeObject(this.items); } 
     set { this.items = JsonConvert.DeserializeObject<List<string>>(value); 
    } 

    public ItemsModel() 
    { 
     this.itemsList = new List<KeyValuePair<string, string>>(); 
     this.items = new List<string>(); 
    } 
} 

而且页面的视图模型包含了这样一个实例:

public class MyViewModel 
{ 
    // Assorted stuff 
    public ItemsModel itemsModel; 
} 

当控制器构建模式,在HttpGet,它用两个列表填充ItemsModel对象。在页面视图包括部分,传递itemsModel:

@{Html.RenderPartial("_itemsList", Model.itemsModel); 

在部分中,我们构建了下拉菜单:

@Html.DropDownList("itemsList", new SelectList(Model.itemsList, "Key", "Value") 

我填充使用javascript:

var items = $.parseJSON('@Html.Raw(Model.itemsJson)'); 
var itemsUl = $('#itemsUl'); 
itemsUl.empty(); 
var iTemplate = $('#itemTemplate').html(); 
for (var i=0; i<items.length; i++) 
{ 
    var template = iTemplate.nformat("{item}": items[i]); 
    itemsUl.append($(template)); 
} 

而且就我而言。我的意图是添加JavaScript来处理插入和删除,但在这一点上没有意义。因为当我提交包含未修改列表的页面时,MyViewModel.itemsModel为空。在网络上浏览左右,我已经看到了一些关于如何MVC结合请求项复杂的名单,但他们都不是有关我的问题的帖子,因为没有什么是找MVC被束缚。

我看过Fiddler,并且正在发送的请求包括“... & itemsList = & ...” - 它根本没有发送任何数据。

所以我想知道我是否完全追赶了错误的道路。在表单提交中包含复杂数据的正常方法是什么?我已经读过FormData(),但似乎只适用于Ajax风格的发送,它不影响正常的表单提交。

任何想法?

回答

0

像往常一样,在这些问题中,我只是没有思考问题。

如果我想在表单提交期间在请求中包含一个值,我需要放置一个表单元素。

首先,我删除了并行json属性 - 很容易序列化/反序列化内联,并使其更加明显。所以我的模型变为:

public class ItemsModel 
{ 
    // The list to be displayed in the <select> 
    public List<string> itemsList { get; set; } 

    // The list of selected items 
    public List<string> items { get; set; } 

    public ItemsModel() 
    { 
     this.itemsList = new List<string>(); 
     this.items = new List<string>(); 
    } 
} 

列表只发送给浏览器,它不需要回来,所以它可以直接被注入到一个javascript变量:

var itemsSelect = $('#itemsSelect'); 
itemsSelect.find('option').remove(); 

var itemsList = 
    $.parseJSON('@Html.Raw(JsonConvert.SerializeObject(Model.itemsList))'); 

for (i = 0; i < itemsList.length; i++) 
{ 
    var item = itemsList[i]; 
    itemsSelect 
     .append($("<option>", { value: item }) 
     .text(item ? item : "Select Item")); 
} 

的项目,但是,需要回来。这意味着JSON需要在页面上被注入作为一个隐藏的元素:

@Html.Hidden("itemsJson", JsonConvert.SerializeObject(Model.items)) 

然后在JavaScript中,我们将数据提取到一个数组:

var items = JSON.parse($('#itemsJson').val()); 

然后我建<李>从这些元素中,并将它们添加到我的< ul>。然后,用户从< select中选择一个项目,然后单击“添加”按钮,或者单击< li>中的一个中的“删除”按钮,然后从下拉列表中删除<选项>,然后添加一个< li>至< ul>,或者我从< ul>删除< li>,然后将<选项>添加到< select>。在任何情况下,我需要重新序列化数组,并更新隐藏元素:

$('#itemsJson').val(JSON.stringify(items)); 

然后,当我提交表单,浏览器将在请求中包含一个JSON字符串,命名为“itemsJson”。剩下的唯一问题是如何将这个问题纳入HttpPost行动。有三种可能性:

  1. 我可以从Request [“itemsJson”]中提取值。我不喜欢那样。
  2. 我可以构建一个自定义绑定来反序列化JSON并更新模型。这似乎是不必要的工作。
  3. 或者,我可以在我的Action中添加另一个参数,并在那里反序列化它。

我选择了选项3:

[Authorize] 
[HttpPost] 
public ActionResult Perishable(MyViewModel model, string itemsJson) 
{ 
    if (ModelState.IsValid) 
    { 
     model.itemsModel= new ItemsModel 
     { 
      items= JsonConvert.DeserializeObject<List<string>>(itemsJson) 
     }; 

     ... 
    } 
}