Получение значений из вложенного сложного объекта, который передается частичному представлению

У меня есть ViewModel, который имеет сложный объект как один из его членов. Комплексный объект имеет 4 свойства (все строки). Я пытаюсь создать многоразовый частичный вид, где я могу передать сложный объект и создать его html с помощью html-помощников для его свойств. Это все отлично работает. Однако, когда я отправляю форму, привязка модели не отображает значения обратно в элемент ViewModel, поэтому я ничего не могу получить на стороне сервера. Как я могу прочитать значения, которые пользователь вводит в html-помощники для сложного объекта.

ViewModel

public class MyViewModel
{
     public string SomeProperty { get; set; }
     public MyComplexModel ComplexModel { get; set; }
}

MyComplexModel

public class MyComplexModel
{
     public int id { get; set; }
     public string Name { get; set; }
     public string Address { get; set; }
     ....
}

контроллер

public class MyController : Controller
{
     public ActionResult Index()
     {
          MyViewModel model = new MyViewModel();
          model.ComplexModel = new MyComplexModel();
          model.ComplexModel.id = 15;
          return View(model);
     }

     [HttpPost]
     public ActionResult Index(MyViewModel model)
     {
          // model here never has my nested model populated in the partial view
          return View(model);
     }
}

Вид

@using(Html.BeginForm("Index", "MyController", FormMethod.Post))
{
     ....
     @Html.Partial("MyPartialView", Model.ComplexModel)
}

Частичный вид

@model my.path.to.namespace.MyComplexModel
@Html.TextBoxFor(m => m.Name)
...

как я могу привязать эти данные к отправке формы, чтобы родительская модель содержала данные, введенные в веб-форме, из частичного представления?

спасибо

EDIT: Я понял, что мне нужно добавить "ComplexModel". ко всем моим управляющим именам в частичном представлении (текстовые поля), чтобы он сопоставлялся с вложенным объектом, но я не могу передать тип ViewModel в частичное представление, чтобы получить дополнительный слой, потому что он должен быть общим для принятия нескольких ViewModel типы. Я мог бы просто переписать атрибут name с помощью javascript, но это кажется чрезмерным гетто для меня. Как еще я могу это сделать?

EDIT 2: я могу статически установить атрибут name с новым { Name= "ComplexModel.Name" }, поэтому я думаю, что я в бизнесе, если у кого-то нет лучшего метода?

Ответ 1

Вы можете передать префикс частичному использованию

@Html.Partial("MyPartialView", Model.ComplexModel, 
    new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "ComplexModel" }})

который добавит префикс к атрибуту name элемента управления, так что <input name="Name"../> станет <input name="ComplexModel.Name"../> и будет правильно связываться с typeof MyViewModel при обратной записи

редактировать

Чтобы сделать это немного проще, вы можете заключить это в HTML-помощник

public static MvcHtmlString PartialFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string partialViewName)
{
  string name = ExpressionHelper.GetExpressionText(expression);
  object model = ModelMetadata.FromLambdaExpression(expression, helper.ViewData).Model;
  var viewData = new ViewDataDictionary(helper.ViewData)
  {
    TemplateInfo = new System.Web.Mvc.TemplateInfo 
    { 
      HtmlFieldPrefix = string.IsNullOrEmpty(helper.ViewData.TemplateInfo.HtmlFieldPrefix) ? 
        name : $"{helper.ViewData.TemplateInfo.HtmlFieldPrefix}.{name}"
    }
  };
  return helper.Partial(partialViewName, model, viewData);
}

и использовать его как

@Html.PartialFor(m => m.ComplexModel, "MyPartialView")

Ответ 2

Вы можете попробовать передать ViewModel в частичное.

@model my.path.to.namespace.MyViewModel
@Html.TextBoxFor(m => m.ComplexModel.Name)

Edit

Вы можете создать базовую модель и нажать сложную модель там и передать основанную модель на частичную.

public class MyViewModel :BaseModel
{
    public string SomeProperty { get; set; }
}

 public class MyViewModel2 :BaseModel
{
    public string SomeProperty2 { get; set; }
}

public class BaseModel
{
    public MyComplexModel ComplexModel { get; set; }
}
public class MyComplexModel
{
    public int id { get; set; }
    public string Name { get; set; }
    ...
}

Затем ваше частичное будет выглядеть следующим образом:

@model my.path.to.namespace.BaseModel
@Html.TextBoxFor(m => m.ComplexModel.Name)

Если это не приемлемое решение, вам, возможно, придется подумать о переопределении связующего. Вы можете прочитать об этом здесь.

Ответ 3

Я столкнулся с той же ситуацией, и с помощью таких информативных сообщений изменился мой частичный код, чтобы иметь префикс для сгенерированных во входных элементах, сгенерированных частичным представлением

Я использовал помощник html.partial, дающий имя и объект частичного вида ModelType и экземпляр объекта ViewDataDictionary с префиксом поля Html для конструктора Html.partial.

Это приводит к запросу GET "xyz url" в "Main view" и отображению частичного представления внутри него с элементами ввода, сгенерированными с помощью префикса, например. ранее Name= "Заголовок" теперь становится Name= "MySubType.Title" в соответствующем HTML-элементе, а также для остальных элементов ввода формы.

Проблема возникла, когда запрос POST был отправлен на "URL-адрес xyz", ожидая, что заполненная форма будет сохранена в моей базе данных. Но MVC Modelbinder не привязывал мои данные модели POSTed с заполненными значениями форм, а также потерял ModelState. Модель в viewdata также достигла нулевого значения.

Наконец, я попытался обновить данные модели в форме "Отправлено", используя метод TryUppdateModel, который принимает экземпляр модели и html-префикс, который был ранее передан частичному представлению, и теперь можно видеть, что модель связана со значениями и состояние модели.

Пожалуйста, дайте мне знать, подходит ли этот подход или немного диверсифицирован!