Updating Entity With A Child Using EditorTemplate - asp.net-mvc-3

I have an Entity (QuoteSheet) that contains a child entity (QuoteTask), which is loaded using the EntityFramework. However, I am receiving an error when I submit this form.
I have created an edit page for the QuoteSheet entity, which then uses an EditorTemplate to edit the QuoteTask child entity.
The controller code is as follows:
public ActionResult TestEdit(int Id)
{
var quote = DataContext.QuoteSheets.Where(x => x.ID == Id).FirstOrDefault();
return View(quote);
}
[HttpPost]
public ActionResult TestEdit(Models.QuoteSheet quote)
{
return View(quote);
}
A stripped down version of the view is as follows:
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
#Html.HiddenFor(x => x.JobID);
<div class="sectionHeader">Sheet Details</div>
<div class="sectionContent">
<table>
<tr>
<td width="150">Sheet Desc.</td><td>#Html.TextBoxFor(x => x.Description, new { size = "50" })</td>
</tr>
<tr>
<td>Quantity Required</td><td>#Html.EditorFor(x => x.Quantity)</td>
</tr>
</table>
</div>
<div class="sectionHeader">Tasks</div>
<div class="sectionContent">
<table id="Tasks">
<tr>
<th>Labour Group</th>
<th>Task Description</th>
<th>Total Hrs</th>
<th>Rate</th>
<th>Cost</th>
</tr>
#Html.EditorFor(x => x.QuoteTasks)
</table>
<input type="button" name="AddTasks" id="AddTasks" value="Add" />
</div>
<input type="submit" value="Submit" />
#Html.ValidationSummary()
}
And the EditorTemplate is:
#model Ornavi.Models.QuoteTask
<tr>
<td>#Html.EditorFor(x => Model.LabourGroup)</td>
<td>#Html.EditorFor(x => Model.Description)</td>
<td>#Html.EditorFor(x => Model.TotalHours)</td>
<td>#Html.EditorFor(x => Model.Rate)</td>
<td>#Html.HiddenFor(x => Model.ID)</td>
</tr>
When I submit the form, I am getting the following error:
The EntityCollection has already been initialized. The InitializeRelatedCollection method should only be called to initialize a new EntityCollection during deserialization of an object graph.
This only occurs when I use the EditorTemplate - if I remove the editor template and just submit the main entity, it works fine.
I have placed a breakpoint in the [httppost] TestEdit function, but the exception occurs before it reaches this point.
Any ideas on how to successfully use an EditorTemplate to edit a child entity?

The problem is, that the default modelbinder tries to instantiate your EF class and set the navigation properties when binding the form data to your parameter types.
See some similar questions like this one.
You have two options:
Don't use your EF classes as viewmodels but create own viewmodel classes to pass the data between controller and view.
Don't bind directly to the EF class in your Edit controller action but use a FormCollection parameter and bind yourself with UpdateModel as shown in the linked question.

Related

MVC 5 dynamic model in partial view

I have a few (around 6) lookup tables like degreetype, categorytype, etc. All these table has similar column id, name and isactive fields. I created CRUD stored procedure for each table.
In my MVC5 project, i created a model, repository and DAL to pass data to sp for each.
I also created viewmodel, controller and crud views for each.
I realized the crud (create/edit/detail/list/delete) view pages uses the same html except for calling the model at the top of the page.
Question: Is there any way i can create a partial view of the html and use a dynamic model in the pages?
eg:
#model Microsoft.myorg.viewmodels.degreetypesVM
<!-- need to use a dynamic model (degreetypesVM, categorytypeVM etc) above and create a partial view for the below html -->
#{
ViewBag.Title = "Degree Type";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
<table class="container">
<tbody>
<tr class="row">
<td class="col2" scope="row">
<div class="title-text">
#Html.LabelFor(model => model.Name)
<span title="This field is required." class="warning">*</span>
</div>
</td>
<td class="gray">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</td>
</tr>
..
..
</tbody>
..
..
you can simply write a partial view and pass model in it or just wrap up all common fileds
and write their own model for your partial view

When using AjaxHelper to retrieve a partial view, the embedded data is always the same

We use ASP.NET MVC 5's AjaxHelper and Ajax.BeginForm to request a partial view. That request also needs some JSON data in order to update a map control.
The view rendering part of the process works great (a table body is replaced with the strongly-typed partial view), but the JSON data (embedded into the data-json attribute of the div element as described in this answer and retrieved in my OnSuccess function) always has the same value.
To eliminate the controller code or ViewBag as culprit, I replaced the JSON data (originally retrieved from the ViewBag) with a direct call to DateTime.Now. Sure enough, the same DateTime is printed each time in updateMap() (e.g., 2/11/2016+5:24:42+PM)
I've tried disabling caching, and changing the HTML method to Post, in my AjaxOptions.
In the Parent View (changing the ListBox selection submits the form):
#model string
#{
ViewBag.Title = "Project List";
AjaxOptions ajaxOpts = new AjaxOptions
{
UpdateTargetId = "tableBody",
OnSuccess = "updateMap",
HttpMethod = "Post",
AllowCache = false
};
}
#using (Ajax.BeginForm("GetProjectsData", ajaxOpts))
{
<fieldset>
<legend>Project State</legend>
<div class="editor-field">
#Html.ListBox("selectedStates", ViewBag.StatesList as MultiSelectList,
new { #class = "chzn-select", data_placeholder = "Choose States...", style = "width:350px;", onchange = "$(this.form).submit();" })
</div>
</fieldset>
}
<table class="table">
<thead>
<tr>
<th>
Project Name
</th>
<th>
Project Firm
</th>
<th>
Project Location
</th>
<th>
Building Type
</th>
<th>
Project Budget
</th>
<th></th>
</tr>
</thead>
<tbody id="tableBody">
#Html.Action("GetProjectsData", new { selectedStates = Model })
</tbody>
</table>
<script>
function updateMap() {
var jsonData = $("#geoJsonData").attr("data-json");
var decoded = decodeURIComponent(jsonData);
console.log(decoded); // always prints same value
}
</script>
The partial view:
#model IEnumerable<OurModel>
<div id="geoJsonData" data-json="#Url.Encode(DateTime.Now.ToString())"></div>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.NAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.COMPANY_NAME)
</td>
<td>
#Html.DisplayFor(modelItem => item.STATE)
</td>
<td>
#Html.DisplayFor(modelItem => item.BUILDING_TYPE)
</td>
<td>
#Html.DisplayFor(modelItem => item.BUDGET_AMT)
</td>
</tr>
}
I'm hesitant to jettison the MVC helper classes' pattern of returning a partial view and instead manually render a view into a JSON object. Why is the updated tablebody visible on screen, but when jQuery requests the div element it always has the same data?
Interesting...replacing the div with a good old hidden input element worked. Now fresh data is retrieved each time.
This
<div id="geoJsonData" data-json="#Url.Encode(DateTime.Now.ToString())"></div>
Became this
<input id="geoJsonData" type="hidden" value="#Url.Encode(DateTime.Now.ToString())" />
I wonder why the data-json in the div remained "stale" while the value of the input field did the trick?

Adding controls dynamically in mvc3 using Editortemplates

In my program I am trying to add extra controls dynamically on button click.Is it possible by using EditorTemplates?
This is my EditorTemplates
#model chPayroll.Models.HREducation.HRInfo
#{
var list = (IEnumerable<SelectListItem>)TempData["PassedDivision"];
var list1 = (IEnumerable<SelectListItem>)TempData["Country"];
}
#Html.HiddenFor(x => x.StaffId)
<tr>
<td>#Html.DropDownListFor(x => x.Country, list1, "-select-")</td>
<td>#Html.TextBoxFor(x=>x.Board)</td>
<td>#Html.TextBoxFor(x=>x.Level)</td>
<td>#Html.TextBoxFor(x=>x.PassedYr)</td>
<td>#Html.DropDownListFor(x=>x.PassedDivision,list,"-selected-")</td>
<td><input type="file" name="file"></td>
</tr>
Now I want to add all controls dynamically on button click.
I am calling listeditor from the view.
#model chPayroll.Models.HREducation.HRInfo
<div align="left">
<fieldset style="left:0px">
#using (Html.BeginForm("Addcontrols", "HREduInformation", FormMethod.Post))
{
<table >
<tr>
<th >Country</th>
<th>Board</th>
<th>Level</th>
<th>Passed Year</th>
<th>Division</th>
<th>certificate</th>
</tr>
#Html.EditorFor(m => m.listInfoeditor)
</table>
<input type="submit" value="Add New" id="savechanges" />
}
</fieldset>
</div
You can use Ajax to grab the rendered control Html.
Editing a variable length list
You can also go one step further and avoid the ajax call by using an already rendered template on the client side

MVC unobtrusive validation only being applied to one nested model

I have a form made up of nested models as below:
foreach (var item in Model)
{
<h3>
#item.StageDescription
</h3>
<div class="well">
<table id="Item#(item.ID)" class="WizardOption">
<thead>
<tr>
<some headings here />
</tr>
</thead>
<tbody>
#Html.EditorFor(m => item.WizardOptions,"","WizardOptions",null)
</tbody>
</table>
</div>
}
The WizardOption class has a required field call Display Value:
public class WizardOptionMetaData {
[Required]
public string DisplayValue { get; set; }
}
This works fine for the first table, if I leave a DisplayValue field blank I get the error: "The DisplayValue field is required." and the following markup is rendered:
<input class="description-box" data-val="true" data-val-required="The DisplayValue field is required." id="WizardOptions_0__DisplayValue" name="WizardOptions[0].DisplayValue" type="text" value="">
But any tables after the first one don't get the validation rendered properly:
<input class="description-box" id="WizardOptions_1__DisplayValue" name="WizardOptions[1].DisplayValue" type="text" value="">
Where am I going wrong?
Found the answer on a question I didn't find until after I posted the question:
ASP.NET MVC 3: Generate unobtrusive validation when BeginForm is on the layout
#{
var originalContext = ViewContext.FormContext;
ViewContext.FormContext = new FormContext();
}
<!-- This will generate proper HTML5 data-* validation attributes -->
#Html.TextBoxFor(x => x.Prop1)
#Html.ValidationMessageFor(x => x.Prop1)
#Html.TextBoxFor(x => x.Prop2)
#Html.ValidationMessageFor(x => x.Prop2)
#{
ViewContext.FormContext = originalContext;
}

MVC3 radiobuttons do not behave like they should

I have a problem that's starting to drive me crazy.. I've narrowed my problem down to the following test case:
Model:
public class TestModel
{
public bool TestBool { get; set; }
}
Index View:
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#Html.Action("Test")
Test View:
#model IEnumerable<Test_GUI.Models.TestModel>
#using (Html.BeginForm()) {
<table>
#Html.EditorForModel()
</table>
<input type="submit" value="OK"/>
}
Editor template;
<tr>
<td>
Yes
#Html.RadioButtonFor(m => m.TestBool, Model.TestBool)
</td>
<td>
No
#Html.RadioButtonFor(m => m.TestBool, !Model.TestBool)
</td>
In the TestController, I create two instances of TestModel with a value of false and pass them to the view. But the radiobuttons are rendered as checked, and they also return as true if I post the form..
I've tried many other ways to display the radiobuttons, but nothing seems to work. This seems like to correct way to do it.
I must be able use the current value of the boolean, so I cannot use fixed true or false values in the view. If I do use a fixed true/false, I do get the correct values on the form and in the controller after posting the form..
Try like this:
<td>
Yes
#Html.RadioButtonFor(m => m.TestBool, true)
</td>
<td>
No
#Html.RadioButtonFor(m => m.TestBool, false)
</td>

Resources