I'm trying to teach myself MVC. I mocked up a dummy project to reflect a real problem I have at the moment.
My Model(s):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MvcPersistPartialView.Models {
public class RemarkModels {
public static List<RemarkTemplateModel> GetTemplateAll() {
return new List<RemarkTemplateModel>() {RemarkTemplateModel.Create(1),
RemarkTemplateModel.Create(2),
RemarkTemplateModel.Create(3)};
}
}
public class RemarkModel {
public int ID { get; set; }
public string Content { get; set; }
public int TemplateID { get; set; }
public List<RemarkTemplateModel> Templates { get; set; }
public static RemarkModel Create() {
return new RemarkModel() {
Content = "This is a dummy remark, to learn MVC.",
TemplateID = 1,
Templates = RemarkModels.GetTemplateAll()
};
}
}
public class RemarkTemplateModel {
public int ID { get; set; }
public string Name { get; set; }
public List<RemarkTemplateFieldModel> Fields { get; set; }
public static RemarkTemplateModel Create(int id) {
return new RemarkTemplateModel() {
ID = id,
Name = id.ToString(),
Fields = new List<RemarkTemplateFieldModel>() {RemarkTemplateFieldModel.Create("Label A" + id.ToString(),
id.ToString() + "..."),
RemarkTemplateFieldModel.Create("Label B" + id.ToString(),
id.ToString() + "..."),
RemarkTemplateFieldModel.Create("Label C" + id.ToString(),
id.ToString() + "...")}
};
}
}
public class RemarkTemplateFieldModel {
public string Label { get; set; }
public string Content { get; set; }
public static RemarkTemplateFieldModel Create(string label, string content) {
return new RemarkTemplateFieldModel() { Label = label,
Content = content };
}
}
}
What you see in the above models is a RemarkModel, representing a remark you can save on a person like: "This person hides issues with a big smile. Watch out for that!". You also see a RemarkTemplateModel to accomodate a slightly complex wish of my endusers to fill in certain pieces of information like with surveys. So, this 'RemarkModel' could have template 'Testtemplate A'. Templates consist of fields (RemarkTemplateFieldModel) which could look like: 'Testtemplate A' consists out of the fields: 'Field 'A', field 'B' and field 'C' (while ofcourse some 'Testtemplate B' could consist out of fields 'D', 'E', 'F', 'G'). I hope I made this clear. Please ask if it's not clear. Last model is RemarkModels (the 's' at the back), that's just to retrieve all the mocked up templates quickly.
My controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcPersistPartialView.Controllers {
public class HomeController : Controller {
public ActionResult Index() {
ViewBag.Message = "Welcome to ASP.NET MVC!";
Models.RemarkModel r = Models.RemarkModel.Create();
return View(r);
}
[HttpPost]
public ActionResult Index(Models.RemarkModel remark, Models.RemarkTemplateModel template, List<Models.RemarkTemplateFieldModel> fields) {
if (ModelState.IsValid) {
//Persist to database.
}
ViewBag.Message = "Welcome to ASP.NET MVC!";
Models.RemarkModel r = Models.RemarkModel.Create();
return View(r);
}
[AcceptVerbs("POST")]
public ActionResult TemplateChosen(string selectedTemplateID) {
Models.RemarkTemplateModel selectedTemp = (from temp in Models.RemarkModels.GetTemplateAll()
where temp.ID == Convert.ToInt32(selectedTemplateID)
select temp).FirstOrDefault();
return PartialView("RemarkTemplate", selectedTemp);
}
public ActionResult About() {
return View();
}
}
}
As you can see with parameterless 'Index' I create a RemarkModel and pass it in my strongly typed View Index.cshtml. This view looks like:
@model MvcPersistPartialView.Models.RemarkModel
@using (Html.BeginForm()) {
<fieldset>
<div class="editor-label">
@Html.LabelFor(model => model.Content)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Content)
@Html.ValidationMessageFor(model => model.Content)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Templates)
</div>
<div class="editor-field">
@Html.DropDownListFor(
m => m.TemplateID,
new SelectList(Model.Templates, "ID", "Name"),
new {
id = "templateDdl",
data_url = Url.Action("TemplateChosen", "Home")
}
)
<input type="button" value="Change template" id="template-button" />
</div>
<div id="template">
@Html.Partial("RemarkTemplate", Model.Templates.First())
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
So far so good. I've been on here before to ask help with the DropDownListFor, this works like a charm now (so thank you community). We are getting to the core of my question, thank you for your patience so far. The partial view 'RemarkTemplate' looks like:
@model MvcPersistPartialView.Models.RemarkTemplateModel
<div class="display-field">
@Html.Encode(Model.ID)
</div>
<div class="display-field">
@* @Html.Encode(Model.Name)*@
@Html.EditorFor(m => m.Name)
</div>
@foreach (MvcPersistPartialView.Models.RemarkTemplateFieldModel field in Model.Fields) {
@* <div class="editor-label">
@Html.Encode(field.Label)
</div>
<div class="editor-field">
@Html.TextBox("Content", field.Content)
</div>*@
@Html.Partial("RemarkTemplateField", field)
}
We'll get to the 2nd partial view. Because first I tried this without the partial view 'RemarkTemplateField' and only 2 parameters (remark + template) in the POST Action 'Index'. My problem is that the 'template' parameter's 'fields' property remained 'null'. I also noticed nothing was really posted about the template untill I used an 'EditorFor' for the name property of the template. The 'remark' parameter however contained the correct template ID in it's TemplateID property, so I figured if I could atleast get the fields, the template doesn't matter. So that is when I added the 2nd partial view:
@model MvcPersistPartialView.Models.RemarkTemplateFieldModel
<div class="editor-label">
@Html.Encode(Model.Label)
</div>
<div class="editor-field">
@*@Html.TextBox("Content", Model.Content)*@
@Html.EditorFor(m => m.Content)
</div>
It al displays perfectly. Very nice. But the POSTback to the Index action keeps returning an empty 'template' (well the 'name' of the template is filled in since I made it an EditorFor which is not an option on the real website) and the 'fields' list is empty. As you can see I again changed the Textbox on Content to an EditorFor. But alas, no magic.
So my question is my dear experts, how does one get a more complex model like this bound properly in MVC? How can I get the filled in contents of the fields in the Action of my Controller, so I can ultimatly persist the remark with the correct filled in template to the database? I would be very grateful for your help. Point me to tutorials, if you don't want to spell it out, which can help me. Just give me some starters. I've been at it for a workday now...