Получение значений из вложенного сложного объекта, который передается в частичное представление
У меня есть 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)
...
Как связать эти данные при отправке формы, чтобы родительская модель содержала данные, введенные в веб-форму из частичного представления?
Спасибо
РЕДАКТИРОВАТЬ: я понял, что мне нужно добавить "ComplexModel". ко всем именам моего элемента управления в частичном представлении (текстовые поля), чтобы он отображался на вложенный объект, но я не могу передать тип ViewModel частичному представлению, чтобы получить этот дополнительный слой, потому что он должен быть универсальным, чтобы принимать несколько ViewModel типы. Я мог бы просто переписать атрибут name с помощью javascript, но это кажется мне чрезмерным гетто. Как еще я могу это сделать?
РЕДАКТИРОВАТЬ 2: Я могу статически установить атрибут имени с новым { Name="ComplexModel.Name" }, поэтому я думаю, что я в бизнесе, если у кого-то нет лучшего метода?
5 ответов
Вы можете передать префикс частичному использованию
@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 = name }
};
return helper.Partial(partialViewName, model, viewData);
}
и использовать его как
@Html.PartialFor(m => m.ComplexModel, "MyPartialView")
Если вы используете помощники тегов, partial
помощник тега принимает for
атрибут, который делает то, что вы ожидаете.
<partial name="MyPartialView" for="ComplexModel" />
Используя for
атрибут, а не типичный model
атрибут, приведет к тому, что все поля формы в партиале будут названы с ComplexModel.
префикс.
Вы можете попробовать передать ViewModel в партиал.
@model my.path.to.namespace.MyViewModel
@Html.TextBoxFor(m => m.ComplexModel.Name)
редактировать
Вы можете создать базовую модель, вставить туда сложную модель и передать основанную модель частичному.
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)
Если это неприемлемое решение, возможно, вам придется подумать о переопределении связующего элемента модели. Вы можете прочитать об этом здесь.
Я столкнулся с такой же ситуацией и с помощью таких информативных постов изменил свой частичный код, чтобы иметь префикс для сгенерированных во входных элементах, сгенерированных частичным представлением
Я использовал помощник Html.partial, предоставляющий частичное имя и объект ModelType, а также экземпляр объекта ViewDataDictionary с префиксом поля Html для конструктора Html.partial.
Это приводит к GET-запросу "xyz url" для "Main view" и визуализации частичного представления внутри него с элементами ввода, сгенерированными с префиксом, например, ранее Name="Title" теперь становится Name="MySubType.Title" в соответствующем элементе HTML и таким же для остальные элементы формы ввода.
Проблема возникла, когда POST-запрос был отправлен на "xyz url", ожидая, что заполненная форма будет сохранена в моей базе данных. Но MVC Modelbinder не связывал мои данные модели POST с заполненными значениями формы, а также ModelState также теряется. Модель в viewdata также обнулялась.
Наконец, я попытался обновить данные модели в форме "Публикация", используя метод TryUppdateModel, который принимает экземпляр модели и префикс html, которые ранее были переданы в частичное представление, и теперь можно увидеть, что модель связана со значениями, и состояние модели также присутствует.
Пожалуйста, дайте мне знать, если этот подход хорошо или немного разнообразен!
Я столкнулся с этой проблемой и решением, но использовал ядро dotnet. Поэтому я использовал решение Дэвида Карека и преобразовал его в основной вариант dotnet:
public static class HtmlHelperExtensions
{
public static Task<IHtmlContent> PartialForAsync<TModel, TProperty>(
this IHtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression,
string partialViewName
)
{
if (htmlHelper == null)
{
throw new ArgumentNullException(nameof(htmlHelper));
}
if (expression == null)
{
throw new ArgumentNullException(nameof(expression));
}
if (partialViewName == null)
{
throw new ArgumentNullException(nameof(partialViewName));
}
string name = htmlHelper.GetExpressionText(expression);
object model = htmlHelper.GetModelExpression(expression).Model;
var viewData = new ViewDataDictionary(htmlHelper.ViewData)
{
TemplateInfo =
{
HtmlFieldPrefix = string.IsNullOrEmpty(htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix)
? name
: $"{htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix}{(name.StartsWith('[') ? "" : ".")}{name}"
}
};
return htmlHelper.PartialAsync(partialViewName, model, viewData);
}
public static string GetExpressionText<TModel, TResult>(
this IHtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TResult>> expression)
{
var expressionProvider = htmlHelper.ViewContext.HttpContext.RequestServices
.GetService(typeof(ModelExpressionProvider)) as ModelExpressionProvider;
if (expressionProvider == null)
{
throw new InvalidOperationException("Unable to retrieve ModelExpressionProvider from DI");
}
return expressionProvider.GetExpressionText(expression);
}
public static ModelExpression GetModelExpression<TModel, TResult>(
this IHtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TResult>> expression)
{
var expressionProvider = htmlHelper.ViewContext.HttpContext.RequestServices
.GetService(typeof(ModelExpressionProvider)) as ModelExpressionProvider;
if (expressionProvider == null)
{
throw new InvalidOperationException("Unable to retrieve ModelExpressionProvider from DI");
}
return expressionProvider.CreateModelExpression(htmlHelper.ViewData, expression);
}
}
Использование также немного адаптируется:
@await Html.PartialForAsync(m => m.ComplexModel, "MyPartialView")
Я также добавил небольшую адаптацию, в которой нет добавленных.
если выражение вашей модели начинается с[
уметь работать со списками (например,list[1]
).
Если кто-то знает лучший способ достижения того же результата в ядре dotnet, не стесняйтесь реагировать.