Получение значений из вложенного сложного объекта, который передается частичному представлению
У меня есть 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-префикс, который был ранее передан частичному представлению, и теперь можно видеть, что модель связана со значениями и состояние модели.
Пожалуйста, дайте мне знать, подходит ли этот подход или немного диверсифицирован!