C# MVC - Razor Complex Nested Form Submission not binding - c#

Long time lurker... first time question asker...
I have a complex form which returns null when being submitted. Essentially I am trying to build a database driven forms.
The form contains a list of either sections or questions
A section contains a list of either another section, or questions
Model 1:
public FormViewModel {
public List<FormSetsViewModel> formSets { get; set; }
}
Model 2:
public FormSetsViewModel{
QAViewModel questionAnswerViewModel { get; set; }
SectionViewModel sectionViewModel { get; set; }
bool isQuestion { get; set; }
bool isSection { get; set; }
}
Model 3:
public SectionViewModel {
public List<FormSectionQuestionsViewModel> formSectionQuestions { get; set; }
}
Model 4:
public FormSectionQuestionsViewModel {
public QuestionAnswerViewModel questionAnswers;
public SectionViewModel childSection;
int orderNumber;
}
Model 5:
public QAViewModel {
int id { get; set; }
string answer { get; set; }
string question { get; set;}
}
The views are as follows:
FormViewModel.cshtml
#model FormViewModel
#using (Html.BeginForm("Save", "Forms"))
{
<div class="row">
#Html.EditorFor(model => model.formSetsViewModels)
</div>
<div class="controls">
<input type="submit" value="Confirm" class="button" name="save" />
</div>
}
#model FormSetsViewModel
<div class="control-group">
#if (Model.isQuestion)
{
#Html.EditorFor(m => m.questionViewModel);
}
else
{
#Html.EditorFor(m => m.sectionViewModel);
}
</div>
SectionViewModel.cshtml
#model SectionViewModel
#Html.EditorFor(m => m.formSectionQuestions)
FormSectionQuestionsViewModel.cshtml
#model FormSectionQuestionsViewModel
#if (Model.childSection != null)
{
#Html.EditorFor(m => m.childSection)
}
else
{
#Html.EditorFor(m => m.questionAnswers)
}
QAViewModel.cshtml
#model QAViewModel
<p><div class="question-text-edit">#Html.Raw(Model.questionText)</div>
#Html.TextAreaFor(m => m.answer, new { style = "width: 90%; height: 80px;" })
The controller:
[HttpPost]
public ActionResult Save(int caseID, List<FormSetsViewModel> formSets = null)
{
return Index(caseID);
}
The view works great as a database driven form. However, when I submit the form, it seems that the formsets cannot bind, and returns null.
From Html, it created an input like this:
<input id="formSetsViewModels_d762713a-7a2f-497a-9417-4c6e91d33cb8__sectionViewModel_formSectionQuestions_48e738da-10d3-4518-be59-2493e2b7a7cc__questionAnswers_answer" name="formSetsViewModels[d762713a-7a2f-497a-9417-4c6e91d33cb8].sectionViewModel.formSectionQuestions[48e738da-10d3-4518-be59-2493e2b7a7cc].questionAnswers.answer" type="text" value="">

Finally found the answer!
The variable name for the FormSetsViewModel in the
public ActionResult Save(int caseID, List<FormSetsViewModel> formSets = null)
needs to be formSetsViewModel for the model to be able to be binded.
The other thing is that, some public variables in the class does not have { get; set; } method.
All variables that we want to be bind needs the { get; set; } method. Adding this solve the issue.

Related

Controller not able to read model returned from View in MVC

I am using MVC framework in my project and using model binding to fill a view and then retrieve the values from the view using two controllers.
I am able to fill the view with the model properties but when i am trying to retrieve the values on click of a submit button. The model returns null properties.
Here is the ViewModels:
public partial class QuestionAnswer
{
public string QuestionText { get; set; }
public string QuestionId { get; set; }
public string AnswerText { get; set; }
public string SectionName { get; set; }
public bool IsChecked { get; set; }
}
public partial class pluralQuestionAnswer
{
public Dictionary<int, QuestionAnswer> QnAs { get; set; }
public Dictionary<string, int> SectionNameWithRank { get; set; }
public Dictionary<string, int> SectionNameWithQuestionCount { get; set; }
}
Here are the controllers:
namespace Temp.Controllers
{
public partial class AIPController : Controller
{
public ActionResult FirstInitiative()
{
pluralQuestionAnswer ViewModel = new pluralQuestionAnswer();
//code to fill the model ViewModel
return View(ViewModel);
}
[HttpPost]
public ActionResult SaveSelectedInitiatives(pluralQuestionAnswer ViewModel, string Save)
{
//some code that uses ViewModel
//this is where ViewModel shows all properties as null
return View();
}
}
}
FirstInitiative is used to generate the view by passing a viewModel and SaveSelectedInitiatives is used to get value from the viewModel retrieved from the view.
Here is the View:
#using Models;
#model pluralQuestionAnswer
#{
ViewBag.Title = "FirstInitiative";
int sectionIterator;
int sectionPracticeIterator;
int totalPracticeIterator = 1;
}
<header></header>
<section>
<header>
<h1>First Initiative</h1>
</header>
#using (Html.BeginForm("SaveSelectedInitiatives", "AIP", FormMethod.Post))
{
<div class="inititive-table" style="overflow:auto">
<div class="inititive-table-header">
<div class="initiative-questioncolumn">
Common Minimum Practices
</div>
<div class="initiative-answercolumn">
Your response
</div>
</div>
#for (sectionIterator = 1; sectionIterator <= Model.SectionNameWithRank.Count; sectionIterator++)
{
<div class="initiative-section">
<div class="initiative-section-name">
<span>#Model.SectionNameWithRank.Keys.ElementAt(sectionIterator - 1)</span>
</div>
#for (sectionPracticeIterator = 1; sectionPracticeIterator <= Model.SectionNameWithQuestionCount[Model.SectionNameWithRank.Keys.ElementAt(sectionIterator - 1)]; sectionPracticeIterator++)
{
<div class="initiative-section-question initiative-questioncolumn">
<label>
#*model binding*#
#Html.HiddenFor(x => x.QnAs[totalPracticeIterator].QuestionId)
<span style="width: 20px; float:left">#Html.CheckBoxFor(x => x.QnAs[totalPracticeIterator].IsChecked)</span>
<span style="display: block; margin-left: 20px">#Html.Raw(Model.QnAs[totalPracticeIterator].QuestionText)</span>
</label><br />
</div>
<div class="initiative-section-answers initiative-answercolumn">
#Model.QnAs[totalPracticeIterator].AnswerText
</div>
{totalPracticeIterator += 1;}
}
</div>
}
</div>
<div>
<input type="submit" class="zs-left zs-button zs-button-action zs-atp-button" name="Save" value="Save" />
</div>
}
</section>
In your Dictionary objects, change the key from int type to string type and this should work.
You may want to rename your inputs to match the KeyValuePair schema of your model, so replace below:
#Html.HiddenFor(x => x.QnAs[totalPracticeIterator].QuestionId)
With
#Html.Hidden(string.Format("QnAs[{0}].Key", i), /*<You Need Key's value here> Like: (_kvp.Key) */)
#Html.Hidden(string.Format("QnAs[{0}].Value.QuestionId", i), x.QnAs[/*<You Need Key's value here> Like: (_kvp.Key) */].QuestionId)
You may try, below code to get KeyValuePair:
var _kvp = Model.QnAs.ElementAt(totalPracticeIterator)
So, the posted data from browser would look like:
QnAs[0].Key = 1
QnAs[0].Value.QuestionId = 123
QnAs[1].Key = 32
QnAs[1].Value.QuestionId = 55

Bind Dictionary with list in viewmodel to checkboxes

How do you properly bind a Dictionary and it's values per key to checkboxes?
I can display them in the HTTPGET but binding the selected values again to HTTPPOST doesn't seem to work.
viewmodel
public class EditViewModel
{
public Foo Foo { get; set; }
public Dictionary<Bar, List<BarVersionEditVM>> Matrix { get; set; }
}
public class BarVersionEditVM
{
public int ID { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public bool IsSupported { get; set; }
}
view:
<form asp-action="Edit">
<div class="row">
#foreach (var kvp in Model.Matrix.OrderByDescending(x => x.Key.Name))
{
<div class="col-md-2 col-lg-2">
<fieldset>
<legend>#kvp.Key.Name</legend>
#foreach (var version in kvp.Value)
{
<div>
<input type="checkbox" id="#version.ID" value="#version.IsSupported" name="#version.Name" #(version.IsSupported ? "checked=\"checked\"" : "") />
<label>#version.Version:</label>
</div>
}
</fieldset>
</div>
}
</div>
<input type="hidden" asp-for="#Model.Foo.ID" />
<input type="submit" value="Save" class="btn btn-default" />
</form>
In the View I tried also to rewrite with foreach and using Html helpers, but without success:
#Html.CheckBoxFor(model => model.Matrix[kvpair.Key][i].IsSupported)
controller:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(EditViewModel vm) {
// vm is there but Matrix are null.
// and only the ID of Foo property is filled in.
}
any suggestions?
Unless your Dictionary has simple value types for both the Key and Value (e.g. public Dictionary<string, string>), the DefaultModelBinder requires that the form control name attributes be in the format
<input .... name="Matrix[0].Key" value="..." />
<input .... name="Matrix[0].Value[0].ID" value="..." />
<input .... name="Matrix[0].Value[0].Name" value="..." />
There are no HtmlHelper methods that will generate the correct html to allow binding to your Dictionary.
It is far simpler to create simple view model(s) to with IList<T> properties for the collections. Based on the view you have shown, those models would be
public class EditVM
{
public int FooID { get; set; }
public List<BarVM> Bars { get; set; }
}
public class BarVM
{
public string Name { get; set; }
public List<BarVersionVM> Versions { get; set; }
}
public class BarVersionVM
{
public int ID { get; set; }
public string Name { get; set; } // not clear where you use this property
public string Version { get; set; }
public bool IsSupported { get; set; }
}
and your view would then be
#model EditVM
....
#Html.HiddenFor(m => m.FooID)
#for(int i = 0; i < Model.Bars.Count; i++)
{
<fieldset>
<legend>#Model.Bars[i].Name</legend>
#Html.HiddenFor(m => m.Bars[i].Name) // in case you need to return the view in the POST method
#for(int j = 0; j < Model.Bars[i].Versions.Count; j++)
{
<div>
#Html.HiddenFor(m => m.Bars[i].Versions[j].ID)
#Html.CheckBoxFor(m => m.Bars[i].Versions[j].IsSupported)
#Html.LabelFor((m => m.Bars[i].Versions[j].IsSupported, Model.Bars[i].Versions[j].Version)
</div>
}
</fieldset>
}
<input type="submit" value="Save" />

html.hidden for value not set in asp.net MVC core Razor view

I am working on an asp.net MVc core application. I have a popup with a form element like this:
#using (Html.BeginForm("AddIVR", "ITPVoice", FormMethod.Post, new { role = "form" }))
{
#*#Html.HiddenFor(m =>m.m_newIVR.Account, new { #value= Model.accountID})*#
#Html.Hidden("m.m_newIVR.Account", Model.accountID)
}
I have a viewmodel like this:
public class AccountDetailsViewModel
{
public IVRS m_newIVR { get; set; }
}
and IVRS model class like this:
public class IVRS
{
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
}
I am trying to populate it in my view like this:
#Html.HiddenFor(m =>m.m_newIVR.Account, new { #value= Model.accountID})
but when i see view source, Value is null
I tried using:
#Html.Hidden("m.m_newIVR.Account", Model.accountID)
and it shows m_newIVR.Account populated.
Then I am posting the form to controller this action
[HttpPost]
public ActionResult AddIVR(AccountDetailsViewModel model)
{
return RedirectToAction("AccountDetails", "mycontroller")
}
Although I see that AccountId is populated in view ( using viewsource), but in post action method value of model.m_newIVR.Account is null.
HTML output looks like this:
<div id="edit-ivrs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="display: none;">
<div class="modal-dialog">
<form action="/ITPVoice/AddIVR" method="post" role="form"> <div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Add IVR</h4>
<input id="m_newIVR_Account" name="m_newIVR.Account" type="hidden" value="" />
<input id="AccountId" name="AccountId" type="hidden" value="56f5e3d77ea022a042665be1" />
</div>
<div class="modal-body">
</div>
</div>
My Questions are:
Why html.hiddenfor is not setting value of the model variable?
Although html.hidden is setting value, why it is not accessible in post action method ?
Please suggest.
Now I am able to answer your question why does it works for Html.Hidden but not for Html.HiddenFor.
When you Html.HiddenFor with m =>m.m_newIVR.Account then it always try to set value for hidden field value whatever value available in property m.m_newIVR.Account not the value that you specify using #value = Model.AccountId.
If you want to use HiddenFor the set m_newIVR.Account in ViewModel just use following thing.
#Html.HiddenFor(m =>m.m_newIVR.Account)
Html.Hidden is not strongly type so it not depend on name. You can specify different name and value parameter. In this case It is your responsibility to generate proper name for HiddenField.
My Working Sample
Model
public class AccountDetailsViewModel
{
public string AccountId { get; set; }
public IVRS m_newIVR { get; set; }
}
public class IVRS
{
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("account")]
public string Account { get; set; }
}
Controller Action
[HttpGet]
public IActionResult Index1()
{
AccountDetailsViewModel model = new AccountDetailsViewModel();
//model.AccountId = "1222222";
model.m_newIVR = new IVRS();
model.m_newIVR.Account = "122222";
return View(model);
}
[HttpPost]
public IActionResult Index1(AccountDetailsViewModel model)
{
return View(model);
}
View (Index1.cshtml)
#model WebApplication2.Controllers.AccountDetailsViewModel
#using (Html.BeginForm())
{
#Html.HiddenFor(m =>m.m_newIVR.Account)
<input type="submit" />
}
// Sample Out

How do I pass this child model reference to a virtual ICollection in parent domain model in ASP.NET MVC?

I'm creating a commenting system for my ASP.NET MVC blog engine using a view form that triggers a basic controller action method:
FORM:
#if (User.Identity.IsAuthenticated)
{
//using (Html.BeginForm())
// {
<div class="new_comment">
<h6 id="shortcodes" class="page-header"><i class="fa fa-file-text-o"></i> Leave a comment</h6>
<div class="hline"></div>
<form class="form-horizontal" action="#Url.Action("CreateComment")" method="post" role="form">
<div class="form-group">
<div class="col-sm-4 col-md-4">
<textarea rows="7" class="form-control" name="Message" placeholder="Your Comment Here..."></textarea>
#Html.AntiForgeryToken()
<input type="hidden" name="Slug" value="#Model.Slug"/>
<input type="hidden" name="PostId" value="#Model.Id"/>
<br/>
</div>
</div>
<div class="form-group">
<input type="submit" value="Post Comment" class="btn btn-primary" style="margin-left: 12px"/>
</div>
</form>
</div>
//}
}
CONTROLLER:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateComment([Bind(Include = "PostId,Message,Username,DatePosted")]Comment comment)
{
var post = db.BlogPosts.Find(comment.PostId);
if (post == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
if (ModelState.IsValid)
{
comment.Username = User.Identity.GetUserId();
comment.DatePosted = DateTimeOffset.Now;
db.Comments.Add(comment);
db.SaveChanges();
}
return RedirectToAction("BlogPostDetails", new { slug = post.Slug});
}
I've set breakpoints aside each of the expressions contained within the if statement and confirmed that none of the data values being passed ("PostId, Message, Username, DatePosted") are null, and that db.SaveChances() is commiting changes to the database. Next, here isModels.BlogPosts...
MODELS:
public class BlogPosts
{
public BlogPosts()
{
this.Comments = new HashSet<Comment>();
}
public int Id { get; set; }
public DateTimeOffset Created { get; set; }
public DateTimeOffset? Updated { get; set; }
[AllowHtml]
[Required]
public string Title { get; set; }
public string Slug { get; set; }
[Required]
public string Category { get; set; }
[AllowHtml]
[Required]
public string Body { get; set; }
public string MediaURL { get; set; }
public bool Published { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public int PostId { get; set; }
public string Username { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Message { get; set; }
public DateTimeOffset DatePosted { get; set; }
public Nullable<System.DateTimeOffset> Edited { get; set; }
public virtual BlogPosts BlogPost { get; set; }
public virtual ApplicationUser Author { get; set; }
//public int? ParentId { get; set; }
//[ForeignKey("ParentId")]
//public virtual ICollection<Comment> Children { get; set; }
//public string ParentComment { get; internal set; }
}
And here is the view that fails to execute:
VIEW THAT DOES NOT EXECUTE
#foreach (var item in Model.Comments.OrderByDescending(c => c.DatePosted))
{
<div class="comment">
<p>
#if (item.Username == null)
{
<small>By: Anonymous</small><span>|</span>
}
else
{
<small>By: #Html.DisplayFor(modelItem => item.Username)</small><span>|</span>
}
<small>Date: #Html.DisplayFor(modelItem => item.DatePosted)</small>
#if (item.Edited != null)
{
<span>|</span><small>Updated: #Html.DisplayFor(modelItem => item.Edited)</small>
}
</p>
<div>
#Html.DisplayFor(modelItem => item.Message)
</div>
</div>
if (item.Username == User.Identity.GetUserId() || User.IsInRole("Admin") || User.IsInRole("Moderator"))
{
<div>
#Html.ActionLink("Edit", "_EditComment", new { id = item.Id }) <span>|</span>
#Html.ActionLink("Delete", "_DeleteComment", new { id = item.Id })
</div>
}
<br />
<!--<div class="hline"></div>-->
}
<div>
<input type="button" class="btn btn-primary" value="Return to Blog Roll" onclick="location.href = '#Url.Action("BlogIndex")'">
</div>
<br />
#if (User.Identity.IsAuthenticated || User.IsInRole("Admin") || User.IsInRole("Moderator"))
{
<input type="button" class="btn btn-primary" value="Modify Post" onclick="location.href = '#Url.Action("BlogAdmin")'">
<br />
<br />
}
When setting a breakpoint on the first line in the view above: #foreach (var item in Model.Comments.OrderByDescending(c => c.DatePosted)), the reference to
public virtual ICollection<Comment> Comments within the Models.BlogPosts class remains null (which obviously means the logic in my view fails to execute and no comment is posted).
I am new to ASP.NET MVC, EF Code-First, etc., and clearly do not understand how my controller is failing to pass the comment values in the child model to the public virtual ICollection<Comment> Comments in the parent... How is it that Models.Comment as referenced in my CommentCreate controller contains a value, and the very same virtual reference in my Models.BlogPosts does not?
EDIT: After great feedback from several users on both the cosmetic errors and critical errors in my code, as well as helpful ASP.NET MVC resource pointers, I determined that the null references being passed had to do with incorrect property-naming conventions in my domain models. See answer below.
Youre including PostId however the actual property name is Id. Also you need to show what model your view is receiving. If youre concerened with what youre exposing (e.g. Id, why dont you just mark it as a hidden field?).
In the controller pass in nothing but the model that you want to edit, e.g. keep a controller solely for url routing, keep your model solely for your object and your view should be only for the model youre passing in.
Finally figured out that the BlogPost property in public virtual BlogPosts BlogPost in my Models.Comment domain model needs to be renamed to match the domain model's foreign key: public int PostId. The solution was executed as follows:
Changing the property name to Post,
then manually deleting the null PostId values from the Comment table in my database,
and then running update-database -f command in NuGet.

BeginCollectionItem partial within partial not behaving correctly

I am trying to bind all my model data at once through a form submission in MVC 5 using an edited version of BeginCollectionItem as discussed in Joe Steven's blog here.
The model, Company, has a List<Pa_Ipv4>, the class Pa_Ipv4 in turn has a List<IpAllocation>, I want to access and save to the database all the properties of the IpAllocation in each Pa_Ipv4.
IE: Model.pa_ipv4s[x].requestedIps[x].subnet
The main page is using model Company, which has a partial accepting Pa_Ipv4, which has a partial accepting IpAllocation.
Question 1: In my controller, I'm setting a string property for the first item in the list (requestedIp), but when I submit and postback, the property (allocationType) is null, this property needs to be hard coded as it's for internal use within the DB - why is this being reset?
Reason: The property isn't in the post method, as such what is initially declared is discarded as it's not within the end post.
Possible Solution: Use a hidden property within the form so that it is present when the form is posted and the user cannot access the property.
Question 2: BeginCollectionItem is naming attributes appropriately, IE: pa_ipv4s[8e075d50-a5fb-436f-9cef-85abfb6910e3].requestedIps[b693b83c-b6b1-4c42-b983-4d058e766d4c].subnet, but only the initial model, it's then ignoring any others created, what have I done wrong?
Reason: The GUID needed for a prefix generated by the Pa_Ipv4 sections BeginCollectionItem is not able to be accessed by the IpAllocation BeginCollectionItem, as such only the initial content has the correct prefixes, anything added hereafter misses the necessary prefix.
Another potential solution is essentially the same concept, but instead of using a div, use html data attribute instead so that it's accessible.
I think both of the issues I'm experiencing are to do with how I've set my controller up, but I've included the Views and Model below as well. The model contains all the properties, lots of these have been removed in my views to save space as these are not causing the issue.
Create
#model Company
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
<div class="jumboservice">
<div data-role="page">
<div data-role="header">
<h2>PA IPv4 Request Form</h2>
</div>
<div class="ui-content" data-role="main">
<h3>Company Details</h3>
<div class="ui-grid-c ui-responsive">
<div class="ui-block-a">
<p class="lblStyle">Company Name</p>
<span>
#Html.EditorFor(m => m.name)
#Html.ValidationMessageFor(m => m.name)
</span>
</div>
</div>
</div>
<br />
#foreach (var i in Model.pa_ipv4s)
{
#Html.Partial("Pa_IPv4View", i)
}
<br />
<div data-role="main" class="ui-content">
<div data-role="controlgroup" data-type="horizontal">
<input type="submit" class="ui-btn" value="Create" />
</div>
</div>
</div>
</div>
}
<script type="text/javascript">
$(function () {
$('#addItemRIpM').on('click', function () {
$.ajax({
url: '#Url.Action("RequestedManager")',
cache: false,
success: function (html) { $("#editorRowsRIpM").append(html); }
});
return false;
});
$('#editorRowsRIpM').on('click', '.deleteRow', function () {
$(this).closest('.editorRow').remove();
});
});
</script>
Pa_Ipv4 Partial
#model Pa_Ipv4
#using (HtmlHelpers.BeginCollectionItem.HtmlPrefixScopeExtensions.BeginCollectionItem(Html,"pa_ipv4s"))
{
#Html.AntiForgeryToken()
<div class="ui-grid-c ui-responsive">
<div class="ui-block-a">
<p class="lblStyle">Subnet</p>
</div>
<div class="ui-block-b">
<p class="lblStyle">Size(CIDR)</p>
</div>
<div class="ui-block-c">
<p class="lblStyle">Mask</p>
</div>
<div class="ui-block-d">
</div>
</div>
#*Request IP Address Space List*#
<div id="editorRowsRIpM">
#foreach (var item in Model.requestedIps)
{
#Html.Partial("RequestedIpView", item)
}
</div>
#Html.ActionLink("Add", "RequestedManager", null, new { id = "addItemRIpM", #class = "button" })
}
RequestedIp Partial
#model IpAllocation
<div class="editorRow">
#using (HtmlHelpers.BeginCollectionItem.HtmlPrefixScopeExtensions.BeginCollectionItem(Html, "requestedIps"))
{
<div class="ui-grid-c ui-responsive">
<div class="ui-block-a">
<span>
#Html.TextBoxFor(m => m.subnet)
</span>
</div>
<div class="ui-block-b">
<span>
#Html.TextBoxFor(m => m.cidr)
</span>
</div>
<div class="ui-block-c">
<span>
#Html.TextBoxFor(m => m.mask)
<span class="dltBtn">
Remove
</span>
</span>
</div>
</div>
}
</div>
Controller
public ActionResult Create()
{
var cmp = new Company();
cmp.contacts = new List<Contact>
{
new Contact { email = "", name = "", telephone = "" }
};
cmp.pa_ipv4s = new List<Pa_Ipv4>
{
new Pa_Ipv4
{
ipType = "Pa_IPv4", registedAddress = false, existingNotes = "",
numberOfAddresses = 0, returnedAddressSpace = false, additionalInformation = "",
requestedIps = new List<IpAllocation>
{
new IpAllocation { allocationType = "Requested", cidr = "", mask = "", subnet = "" } // allocationType is null in cmp in the Create[HttpPost]
}
}
};
return View(cmp);
}
public ActionResult Pa_IPv4Manager()
{
return PartialView("Pa_IPv4View", new Pa_Ipv4());
}
public ActionResult RequestedManager()
{
return PartialView("RequestedIpView", new IpAllocation { allocationType = "Requested" }); // allocationType is null in cmp in the Create[HttpPost]
}
// POST: Pa_Ipv4/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Company cmp) //only one requestedIps count regardless of how many add
{
if (ModelState.IsValid)
{
db.companys.Add(cmp);
db.SaveChanges();
return RedirectToAction("Index");
}
Model
[Table("Ipv_Base")]
public class Ipv_Base
{
[Key]
public int ipv_baseId { get; set; }
public int companyId { get; set; }
[ForeignKey("companyId")]
public Company company { get; set; }
public string ipType { get; set; }
[Required]
public bool registedAddress { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string existingNotes { get; set; }
[Required]
public int numberOfAddresses { get; set; }
[Required]
public bool returnedAddressSpace { get; set; }
[DataType(DataType.MultilineText)]
public string additionalInformation { get; set; }
// navigation properties
public virtual IList<IpAllocation> requestedIps { get; set; }
}
[Table("Company")]
public class Company
{
[Key]
public int companyId { get; set; }
[Required]
public string name { get; set; }
[Required]
public string telephone { get; set; }
[Required]
public string regNumber { get; set; }
// navigation properties to keep track of the models that belong to the company
public virtual IList<Pa_Ipv4> pa_ipv4s { get; set; }
}
[Table("IpAllocation")]
public class IpAllocation
{
[Key]
public int ipAllocationId { get; set; }
public int ipv_BaseId { get; set; }
[ForeignKey("ipv_BaseId")]
public Ipv_Base ipv_Base { get; set; }
[Required]
public string allocationType { get; set; }
[Required]
public string subnet { get; set; }
[Required]
public string cidr { get; set; }
[Required]
public string mask { get; set; }
}
public class Pa_Ipv4 : Ipv_Base
{
public Pa_Ipv4()
{
ipType = "pa_ipv4";
}
}
Question 1 Solution:
The issue with Q1 was that the property value I was assigning in the controller wasn't being parsed back from the form post, because the property wasn't there.
Added a hidden field for the property to rectify the pesky null.
<div class="ui-block-a">
<span>
#Html.HiddenFor(m => m.allocationType)
#Html.TextBoxFor(m => m.subnet, new { #class = "checkFiller" })
</span>
</div>
Question 2 Solution:
The issues that I was facing with the GUID of the first model being attached as the prefix to the second model was largely due to how I was sending data using AJAX to the controller action method.
The code snippets shown below fix the issues and display the correctly bound GUIDs.
name="pa_ipv4s[f7d8d024-5bb6-451d-87e3-fd3e3b8c1bba].requestedIps[d5c08a43-f65e-46d1-b224-148225599edc].subnet" is now being shown on the dynamically created model properties, not just the initially created.
When running in debug in visual studio and hovering over the model, digging down into the data shows the correct counts of the model lists.
Controller ActionMethod:
public ActionResult ExistingManager(string containerPrefix)
{
ViewData["ContainerPrefix"] = containerPrefix;
return PartialView("ExistingIpView", new IpAllocation { allocationType = "Existing" });
}
AJAX GET Method calling Controller ActionMethod:
$('#addItemEIpM').on('click', function () {
$.ajax({
url: '#Url.Action("ExistingManager")',
cache: false,
data: 'containerPrefix=' + $('#addItemEIpM').data('containerprefix'),
success: function (html) {
$("#editorRowsEIpM").append(html);
}
});
return false;
});

Categories