2014-01-21 1 views
0

У меня есть небольшая функциональность, которую мне нужно разместить на нескольких страницах, поэтому я реализовал ее как частичное представление.Связывание сложных объектов во время формы post

В этом частичном представлении у меня есть выпадающий список, кнопка добавления и «элементы», каждая из которых включает кнопку удаления. Функциональность очевидна. Нажатие на любую из кнопок удаления удаляет связанный элемент, выбирая элемент из раскрывающегося списка и нажав кнопку add, добавляет этот элемент.

Сложность в том, что это должно произойти полностью в javascript - изменения в списке элементов должны происходить полностью на стороне клиента, и на сервере ничего не происходит до тех пор, пока не будет представлена ​​форма в целом. (То есть, мы не хотим обновлять сервер через ajax при каждом изменении, мы хотим собирать изменения и отправлять их в toto на форме submit.)

Вторая сложность заключается в том, что эта форма должна быть довольно значительно снижается с минимальными требованиями к родительскому виду.

Так я создал ViewModel для частичного:

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>(); 
    } 
} 

И ViewModel для этой страницы содержит экземпляр этого:

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 имеет значение NULL. Просматривая в Интернете, я видел несколько сообщений о том, как MVC связывает элементы Request с сложными списками, но ни одна из них не относится к моей проблеме, потому что ничто не попадает в MVC для привязки.

Я смотрел в Fiddler, и запрос, который отправляется, включает в себя «... & itemsList = & ...» - он не отправляет никаких данных вообще.

Так что мне интересно, полностью ли я преследую неправильный путь. Каков нормальный способ включения сложных данных в форму submit? Я прочитал в FormData(), но, похоже, применяется только к отправке в стиле Ajax, это не влияет на нормальную форму submit.

Любые идеи?

ответ

0

Как обычно, в таких проблемах я просто не думал о вещах.

Если я хочу, чтобы значение было включено в запрос во время отправки формы, мне нужно ввести элемент формы.

Во-первых, я удалил свойства параллельного json - достаточно просто сериализовать/десериализовать inline, и это делает более очевидным то, что происходит.Так что моя модель становится:

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>(); 
    } 
} 

Список отправляется только в браузер, он не должен возвращаться, так что он может быть введена прямо в яваскрипт переменные:

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()); 

А потом я строю < li> от этих элементов, и добавьте их в мой < ul>. Затем пользователь выбирает элемент из списка <> и нажимает кнопку «Добавить» или нажимает кнопку «удалить» в одном из < li>, и я либо удаляю опцию <> из выпадающего меню, либо добавляет < li> to < ul>, или я удаляю < li> от < ul> и добавьте опцию <> в < выберите>. В любом случае, мне нужно reserialize массива и обновить скрытый элемент:

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

Затем, когда я отправить форму, браузер будет включать в себя строку JSON в запросе, названную «itemsJson». Единственный оставшийся вопрос - как это сделать в действии HttpPost. Есть три возможности:

  1. Я мог бы просто извлечь значение из запроса ["itemsJson"]. Мне это не нравится.
  2. Я мог бы создать пользовательскую привязку для десериализации JSON и обновления модели. Это кажется ненужной работой.
  3. Или я мог бы просто добавить еще один аргумент в свое действие и десериализировать его там.

Я выбрал вариант 3:

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

     ... 
    } 
} 
Смежные вопросы