Model retains it's value after Submit to server - asp.net-mvc

I'm facing this weird problem ,unable to make sense of it,I have a form which accepts Person Id and then reads the data from an API and fills the UI for person Edit purposes.
Here is the markup of this form,I'm guessing its has something to do with Model binding as I have two Form tag and both having the same Model Id.
#using (Html.BeginForm("UpdatePerson", "Person", FormMethod.Get))
{
<table>
<tr>
<td colspan="2">
<h3>Read Person for Edit</h3>
</td>
</tr>
<tr>
<td>
<label>#Html.LabelFor(m => m.Id)</label>
</td>
<td>
#Html.TextBoxFor(m => m.Id)
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" name="btnReadPerson" value="Read Person" />
</td>
</tr>
</table>
}
#using (Html.BeginForm("UpdatePerson", "Person", FormMethod.Post))
{
<table>
<tr>
<td>
<label>#Html.LabelFor(m => m.Id)</label>
</td>
<td>
#Html.TextBoxFor(m => m.Id, new { #readonly = "readonly" })
</td>
</tr>
<tr>
<td>
<label>#Html.LabelFor(m => m.Type)</label>
</td>
<td>
#Html.TextBoxFor(m => m.Type)
</td>
</tr>
I have stripped the view,I tried to kept it brief.
Below is the Action which handles the Get
[HttpGet]
[ActionName("UpdatePerson")]
public ActionResult UpdatePersonRead(PersonEditModel model)
{
if (model.Id.HasValue)
{
var apiClient = new ApiClient (ApiVersions.v1);
var segmentReplaceList = new Dictionary<string, string> { { "{id}", model.Id.Value.ToString() } };
bool ApiHitStatus = false;
var result = apiClient.MakeAPIRequest(out ApiHitStatus, ResourceUriKey.Person, segmentReplaceList, HttpVerbs.Get, string.Empty);
model = new PersonEditModel();
if (ApiHitStatus)
{
var personToBeUpdated = JsonConvert.DeserializeObject<RootChildPerson>(result);
if (personToBeUpdated != null)//Assigning json obj to model
{
model.NameFirst = personToBeUpdated.name_first;
model.NameLast = personToBeUpdated.name_last;
model.NameMiddle = personToBeUpdated.name_middle;
model.SocialSecurityNumber = personToBeUpdated.social_security_number;
model.SubType = PersonHelper.SubTypeValue(personToBeUpdated.sub_type);
model.Type = "person";
model.DateOfBirth = personToBeUpdated.date_of_birth;
model.Id = personToBeUpdated.id;
}
}
}
return View(model);
}
Now since the Person Id 4 does not corresponds to any person ,so I receive Null json object which upon conversion to C# class results in an empty (not null because it has every property set to null or empty) personToBeUpdated object which is then assigned to the model,I have checked model.Id becomes null in the Controller and even in the View ,but somehow it assigns input value that is 4 (it was null) to both Person Id textboxes.
Kindly let me know whats happening here.

Well as commented by #StephenMuecke ,So I cleared model before updating it.
model = new PersonEditModel();
ModelState.Clear();
Its also interesting to note that view takes data from ModelState instead of current specified model,
HtmlHelpers controls (like .TextBoxFor() etc.) don't bind to model values on Postback, but rather get their value directly out of the POST buffer from ModelState.
Taken from ASP.NET MVC Postbacks and HtmlHelper Controls ignoring Model Changes

Related

Selecting multiple from #Html.ListBoxFor

I have added #Html.ListBoxFor i am able to bind it properly but unable to get multiple selected items after submit. Am i missing something ?
// Below is the code for binding
public ActionResult Create()
{
var cities = So.BL.City.GetCities();
SelectList cityList = new SelectList(cities, "Id", "Name", cityId);
TempData["Cities"] = cityList;
return View("Create");
}
[HttpPost]
public ActionResult Create([Bind(Include="Keywords,Cities")] So.Entities.Approval filter)
{
if (ModelState.IsValid)
{
}
return View(filter);
}
Below is the view file code. I dont have a view model just entities
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<table style="width: 100%">
<tr>
<td>
Cities:
</td>
</tr>
<tr>
#* <td>
#Html.DropDownList("Cities", (SelectList)TempData["Cities"])
</td>*#
</tr>
<tr>
<td>
#Html.ListBoxFor(model => model.Id, new SelectList(TempData["Cities"] as MultiSelectList, "Value","Text",new { style = "width:250px" })))
</td>
</tr>
<tr>
<td>
#Html.LabelFor(model => model.Keywords)
</td>
</tr>
<tr>
<td>
#Html.EditorFor(model => model.Keywords)
</td>
</tr>
<tr>
<td>
</td>
</tr>
<tr>
<td>
<input type="submit" value="Create" class="button" />
</td>
</tr>
</table>
Assuming you have changed the ID property to typeof int[] as per the comments, you still have problems, the main one being that your POST method has
[Bind(Include="Keywords,Cities")]
which excludes binding of the ID property so it will always be null on post back. When ever you use the [Bind] attribute you should reconsider what you doing and use a view model to display/edit just the properties you want, including a property for the SelectList.
You also have some pointless code in the #Html.ListBoxFor() method. TempData["Cities"] is already a SelectList so new SelectList(TempData["Cities"] as MultiSelectList, "Value","Text" is converting the SelectList to a MultiSelectList and then creating a new SelectList form it. All you need is
#Html.ListBoxFor(model => model.ID, (SelectList)TempData["Cities"], new { style = "width:250px" })
In addition, the 3rd parameter in this
SelectList cityList = new SelectList(cities, "Id", "Name", cityId);
is not required (not sure where you declared cityId because as it is, your code does not compile). The ListBoxFor() method selects the options based on the value of your ID property, and ignores the selectedValue parameter in the SelectList constructor.
Finally, in your POST method, if the model is not valid you return the view. You need to reassign the value of TempData["Cities"] or this will be null in the view and throw an exception.
Like Andrei is suggesting, you need to bind the selected value to an array type.
Front End:
<div class="DualListBoxDIV">
#Html.ListBoxFor(model => model.RelatedNewsArticlesSelected, new MultiSelectList(Model.AssignedRelatedNewsArticles, "Value", "Text", Model.RelatedNewsArticlesSelected), new { #class = "SelectedBox", Size = 10 })
</div>
Back End:
public string[] RelatedNewsArticlesSelected { get; set; }
public IEnumerable<SelectListItem> AssignedRelatedNewsArticles { get; set; }
public IEnumerable<SelectListItem> UnassignedRelatedNewsArticles { get; set; }

how to prepopulate the selected value in Db in a #html.Dropdown in mvc

I want to pre-populate the selected value stored in the DB in the Dropdown list in mvc.
Controller
public ActionResult POCallDown(int? skuid)
{
var supplierlist = db.QuoteMasters.Include(e => e.CommunicationMode).Include(e => e.SKUMaster).Include(e => e.EmployeeMaster).Include(e => e.SupplierMaster).Include(e => e.CreditTerm);
var SupplierforSKU = (from supplierdetails in supplierlist.ToList()
where skuid.HasValue && skuid.Value == supplierdetails.SKU
select supplierdetails).ToList();
foreach(var cred in SupplierforSKU)
{
ViewBag.CreditId = new SelectList(db.CreditTerms, "CreditId", "Description",cred.CreditTermId);
}
return View(SupplierforSKU);
}
View
<table>
<th>Credit</th>
<tr><td> #Html.DropDownList("CreditId")</td></tr>
</table>
I get the first value in the db for all the rows in the view. Please help. I tried using this Populate dropdown but it dowsnot give me the desired result.
Try this :
<table>
<th>Credit</th>
<tr>
<td>
#Html.DropDownList("CreditId", null, "--Select One--", new { #id = "CreditId", #name ="CreditId"})
</td>
</tr>
</table>

Passing value from EditorFor to controller

This is my view where education is the list in the model.
#using chpayroll.Models.CustInformations
#model CustInfoExtract
#Html.HiddenFor(x => x.flag, new { #id = "flag" })
#Html.HiddenFor(x => x.StaffId)
<table style=" width:730px">
<tr>
<th>Country</th>
<th>Board</th>
<th>Level</th>
<th>PassedYear</th>
<th>Division</th>
</tr>
<tr>
#Html.EditorFor(x => x.education)
</tr>
<tr>
<td><input type="submit" value="Add Another" id="addedu"/> </td>
</tr>
</table>
I have editor template as below
#using staffInfoDetails.Models
#model staffInfo.education
#Html.HiddenFor(x=>x.staffId)
<tr>
<td >#Html.DropDownListFor(x => x.country, Model.countryList, "--select--", new { #id="country"})</td>
<td>#Html.TextBoxFor(x => x.board, new { #id="board"})</td>
<td>#Html.TextBoxFor(x => x.level, new { #id="level"})</td>
<td>#Html.TextBoxFor(x => x.passedYr, new { #id="passedYr"})</td>
<td>#Html.DropDownListFor(x => x.passedDiv, Model.passedDivList, "--select--", new { #id="division"})</td>
</tr>
I am trying to pass model from controller to view and back from view to controller. While I was passing model to view, the education list passed, but, when i tried to pass model from view to controller, everything else passed except for the education list. How can I solve this problem ?
Only the selected value from the drop down list will be posted back so you'll need to re-populate your drop down list if validation fails (ie. if the View has to be re-displayed).
Your POST action might look something along the lines of the following:
[HttpPost]
public ActionResult Home(CustInformations viewModel)
{
if (!ModelState.IsValid)
{
// Re-populate drop-down list and redisplay form
viewModel.DropdownListOptions = _repository.getEductionList();
return View(viewModel);
}
// Validation passed
// Save, update, etc and redirect to new page
}

Why is the view not refreshed? (.Net MVC)

I am learning .Net MVC. I have a page where I show productlines. I want to filter the productlines by their suppliers via a dropdownlist.
My controller:
public class ProductlineController : Controller
{
SupplierRepository sr = new SupplierRepository();
ProductlineRepository pr = new ProductlineRepository();
public ActionResult Default()
{
SupplierModel sm = new SupplierModel();
List<Supplier> suppliers = sr.GetAll();
sm.Suppliers = (from s in suppliers select new SelectListItem {
Text = s.Name,
Value = s.Id.ToString()
}).ToList();
sm.Productlines = pr.GetAll();
return View("List", sm);
}
[HttpPost]
public ActionResult SupplierDropUsed(int id)
{
SupplierModel sm = new SupplierModel();
List<Supplier> suppliers = sr.GetAll();
sm.Suppliers = (from s in suppliers
select new SelectListItem
{
Text = s.Name,
Value = s.Id.ToString()
}).ToList();
Supplier supplier = sr.GetById(id);
sm.Productlines = supplier.Productlines.ToList();
return View("List", sm);
}
}
The default action shows all productlines. SupplierDropUsed is called when dropdownlist is changed.
The view:
#model RyfMvcTestApplication1.Models.SupplierModel
#{
Layout = null;
}
List
<script type="text/javascript">
function supplierDropChanged() {
$.post("Productline/SupplierDropUsed", { id: $('#SupplierDrop').val() });
}
</script>
<div><strong>Filter by supplier</strong></div>
<br />
<div>
#Html.DropDownList("SupplierDrop", Model.Suppliers, "Select supplier", new { onChange = "supplierDropChanged()" })
</div>
<br />
<br />
<table>
<tr>
<th style="width:50px; text-align:left">Id</th>
<th style="text-align:left">Name</th>
<th style="text-align:left">Supplier</th>
</tr>
#foreach (var item in Model.Productlines) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Id)
</td>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DisplayFor(modelItem => item.Supplier.Name)
</td>
</tr>
}
</table>
When I select a supplier, the javascript and controller action are executed (I checked in debug mode). I also get the correct supplier id. But the view is never refreshed. I still see the list with all productlines.
You're doing a post via the jquery post method which submits the request and that is why your debug action is called but that result that is returned from the post call is never used to update your UI.

ASP.NET MVC FormCollection TextArea

I have a textarea that represents a description field. The descriptions have commas so when trying to split the field's descriptions the data is not parsed correctly. How can I get each row's description correctly.
var DescList = FormValues["Item.Description"].Split(',').Select(item => item).ToList<string>();
//will not work for obvious reasons. Comma delimited FormCollection has commas to identify separate row data.
It seems like Microsoft designed the FormsCollection without the textarea control in mind. A text area with commas will not work when trying to access each value. What is interesting is that the _entriestables property has it in the perfect format but they chose to make it a private property. Very frustrating.
`
Here is the important part of my viewmodel.
public class TenantViewModel
{
public Tenant Tenant { get; set; }
public Site Site { get; set; }
}
My view is populated like this:
if (Model != null && Model.Tenant != null && Model.Tenant.Site != null && Model.Tenant.Site.Count() > 0)
{<div class="detailsbox_view">
<table id="tblTenantSites">
<tr>
<th>#Html.LabelFor(item => item.Site.Title)</th>
<th>#Html.LabelFor(item => item.Site.Description)</th>
</tr>
#foreach (var Item in Model.Tenant.Sites)
{
<tr>
#Html.HiddenFor(modelItem => Item.SiteId)
<td>
#Html.EditorFor(modelItem => Item.Title)
</td>
<td>
#Html.TextAreaFor(modelItem => Item.Description, new {#width="400" })
</td>
</tr> }
</table>
As you see this site table is a child of Tenant object. This child record does not get automatically updated using this method but the Tenant data does automatically get updated. This is the reason I tried the FormColelction instead.
Is there something I am missing to make this work?
try with this useful function
ValueProviderResult Match=FormCollection.GetValue("ValueProvider");
When you have multiple fields with the same name attribute, they'll come back into your FormCollection as an array. So upon posting a view like this:
<form action="/Home/MyAction">
<textarea id="row_one_description" name="description">
First row's description
</textarea>
<textarea id="row_two_description" name="description">
Second row's description
</textarea>
<input type="submit" value="Submit" />
</form>
you could do something like this in your action
[HttpPost]
public ActionResult MyAction(FormCollection collection)
{
var descriptionArray = collection["description"];
string firstRowDescription = descriptionArray[0];
string secondRowDescription = descriptionArray[1];
}
I must note that this is not the recommended way of dealing with posted data. You should instead be building your view using data from a view model and using strongly typed html helpers to render your controls. That way when you post, your action can take the ViewModel as a parameter. Its properties will be automatically bound and you will have a nice object to play with.
[HttpPost]
public ActionResult MyAction(MyViewModel viewModel)
{
foreach (var row in viewModel.Rows)
{
string description = row.Description;
}
}
EDIT
I'm still assuming a lot about your ViewModel but perhaps try this:
<table id="tblTenantSites">
<tr>
<th>#Html.LabelFor(model => model.Site.Title)</th>
<th>#Html.LabelFor(model => model.Site.Description)</th>
</tr>
#for (var i = i < Model.Tenants.Sites.Count(); i++) {
<tr>
#Html.HiddenFor(model => model.Tenants.Sites[i].SiteId)
<td>
#Html.EditorFor(model => model.Tenants.Sites[i].Title)
</td>
<td>
#Html.TextAreaFor(model => model.Tenants.Sites[i].Description, new { #width="400" } )
</td>
</tr>
}
</table>
You could also try ,
string Match=FormCollection.GetValue("ValueProvider").AttemptedValue;

Resources