MVC: POST controller method get empty Dictionary - c#

My GET method of controller construct and fill Model, which including Dictionary<int, MyClass>, and transmit that to View. But after, POST controller method get not null model with empty Dictionary.
Model:
public class CheckBoxItem
{
public string Name { get; set; }
public double Data { get; set; }
public bool Selected { get; set; }
}
public class CreateNewEventModel
{
[Required(ErrorMessage = "Error text")]
[Display(Name = "Header name")]
public string EventName { get; set; }
public Dictionary<int, CheckBoxItem> CheckBoxDataItems { get; set; }
public CreateNewEventModel()
{
CheckBoxDataItems = new Dictionary<int, CheckBoxItem>();
}
}
Controller:
public ActionResult CreateEvent()
{
CreateNewEventModel model = new CreateNewEventModel();
// FILL MODEL
foreach (var user in db.UsersInfo.ToList())
{
model.CheckBoxDataItems.Add(user.Id, new CheckBoxItem()
{
Name = user.Name,
Data = 0,
Selected = false
});
}
// THERE IS FULL MODEL
return View(model);
}
[HttpPost]
public ActionResult CreateEvent(CreateNewEventModel model)
{
// THERE IS model.Event name include text
// BUT model.CheckBoxDataItems is empty
if (ModelState.IsValid)
{
...
return View(model);
}
return View(model);
}
View:
#model HLyaa.Models.CreateNewEventModel
#{
ViewBag.Title = "Create Event";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Создание события</h2>
#if (Model.CheckBoxDataItems.Count() != 0)
{
using (Html.BeginForm("CreateEvent", "Events", FormMethod.Post))
{
#Html.ValidationSummary()
<div>
#Html.LabelFor(model => model.EventName)
<div>
#Html.EditorFor(model => model.EventName)
</div>
</div>
<table>
#foreach (var kvpair in Model.CheckBoxDataItems)
{
<tr>
<td>
#Html.CheckBoxFor(model => model.CheckBoxDataItems[kvpair.Key].Selected)
</td>
<td>
#Html.DisplayFor(model => model.CheckBoxDataItems[kvpair.Key].Name)
#Html.HiddenFor(model => model.CheckBoxDataItems[kvpair.Key].Selected)
#Html.HiddenFor(model => model.CheckBoxDataItems[kvpair.Key].Name)
</td>
<td>
#Html.TextBoxFor(model => model.CheckBoxDataItems[kvpair.Key].Data, new { #type = "number" })
</td>
</tr>
}
</table>
<br />
<input type="submit" value="Next" />
}
}
How I can transmit data inside dictionary from View to Controller?

Dictionary no, List/Array yes, but you will have to make some modifications.
Modify models
public class CheckBoxItem {
public int UserId { get; set; }
public string Name { get; set; }
public double Data { get; set; }
public bool Selected { get; set; }
}
public class CreateNewEventModel {
[Required(ErrorMessage = "Error text")]
[Display(Name = "Header name")]
public string EventName { get; set; }
public List<CheckBoxItem> CheckBoxDataItems { get; set; }
public CreateNewEventModel() {
CheckBoxDataItems = new List<CheckBoxItem>();
}
}
Modify GET method CreateEvent
public ActionResult CreateEvent() {
var model = new CreateNewEventModel();
//...FILL MODEL
foreach (var user in db.UsersInfo.ToList()) {
model.CheckBoxDataItems.Add(new CheckBoxItem() {
UserId = user.Id,
Name = user.Name,
Data = 0,
Selected = false
});
}
// THERE IS FULL MODEL
return View(model);
}
Update View
<table>
#for (var i = 0; i < Model.CheckBoxDataItems.Count; i++) {
<tr>
<td>
#Html.CheckBoxFor(model => model.CheckBoxDataItems[i].Selected)
</td>
<td>
#Html.DisplayFor(model => model.CheckBoxDataItems[i].Name)
#Html.HiddenFor(model => model.CheckBoxDataItems[i].UserId)
#Html.HiddenFor(model => model.CheckBoxDataItems[i].Selected)
#Html.HiddenFor(model => model.CheckBoxDataItems[i].Name)
</td>
<td>
#Html.TextBoxFor(model => model.CheckBoxDataItems[i].Data, new { #type = "number" })
</td>
</tr>
}
</table>
CheckBoxDataItems should be populated now when you post it to controller

As I answered in this post, you need to call "ToArray()[*]" on your dictionary before accessing its key and value so you can specify an index that's used by the ASP model binder to send your data back to the controller. ;)

Related

The model item passed into the ViewDataDictionary is of type Models but needs ViewModels

I've been struggling with this error for a few days now for an assignment and I can't get it fixed, I've shown it to another person and we are bewildered as to what could be the issue? "InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'FYP_RSVP_MGMT.Models.GuestList', but this ViewDataDictionary instance requires a model item of type 'FYP_RSVP_MGMT.ViewModels.GuestListViewModel'."
GuestListController:
public class GuestListController : Controller
{
public IActionResult Index()
{
GuestListViewModel guest = new GuestListViewModel();
return View("Index", guest);
}
public IActionResult CreateUpdate(GuestListViewModel guest)
{
if(ModelState.IsValid)
{
using (var db = DbHelpers.GetConnection()) {
if(guest.EditableGuest.GuestID == null)
{
/* Count the existing IDs and adds the next ID */
guest.EditableGuest.GuestID = guest.Guests.Count;
db.Insert<GuestList>(guest.EditableGuest);
}
/* If the guest already exists, we are updating the details*/
else
{
GuestList dbItem = db.Get<GuestList>(guest.EditableGuest.GuestID);
TryUpdateModelAsync<GuestList>(dbItem, "EditableGuest");
db.Update<GuestList>(dbItem);
}
}
return RedirectToAction("ViewGuestList");
}
else
{
return View("Index", new GuestList());
}
}
GuestListViewModel
public class GuestListViewModel
{
public GuestListViewModel()
{
using (var db = DbHelpers.GetConnection())
{
this.EditableGuest = new GuestList();
this.Guests = db.Query<GuestList>("Select * From GuestList").ToList();
}
}
public List <GuestList> Guests { get; set; }
/* Holds any instance of an object that is being added/edited/deleted etc */
public GuestList EditableGuest { get; set; }
}
GuestList Model
public class GuestList
{
public int? GuestID { get; set; }
[Required]
public string GuestName { get; set; }
[Required]
public string GuestType { get; set; }
[Required]
public string ContactDetails { get; set; }
public bool PlusOne { get; set; }
public string PlusOneName { get; set; }
[Required]
public string Response { get; set; }
}
Index.cshtml - where form is being displayed
#model FYP_RSVP_MGMT.ViewModels.GuestListViewModel
#{
ViewData["Title"] = "Guest RSVP";
}
#using (var form = Html.BeginForm("CreateUpdate", "GuestList", FormMethod.Post))
{
<div class="container" id="GuestRSVP">
<br/>
<br/>
<br/>
<h3> Welcome to [Bride] and [Groom]s Wedding</h3>
<h4>[Church Location]</h4>
<h4>[Wedding Date and Time]</h4>
<div class="container" id="RSVPTable" style="align-content:center">
<table>
<tr>
<td>Guest Name: </td>
<td>#Html.TextBoxFor(m => m.EditableGuest.GuestName)</td>
<td>#Html.HiddenFor(m => m.EditableGuest.GuestID)</td>
</tr>
<tr>
<td>Guest Type: </td>
<td>#Html.DropDownListFor(m => m.EditableGuest.GuestType, new List<SelectListItem> { new SelectListItem { Value = "Guest", Text = "Guest" }, new SelectListItem { Value = "Wedding Party", Text = "Wedding Party" } })</td>
</tr>
<tr>
<td>Contact Details: </td>
<td>#Html.TextBoxFor(m => m.EditableGuest.ContactDetails)</td>
</tr>
<tr>
<td>Plus One: </td>
<td>#Html.CheckBoxFor(m => m.EditableGuest.PlusOne)</td>
</tr>
<tr>
<td>Plus One Name: </td>
<td>#Html.TextBoxFor(m => m.EditableGuest.PlusOneName)</td>
</tr>
<tr>
<td>RSVP Response:</td>
<td>#Html.DropDownListFor(m => m.EditableGuest.Response, new List<SelectListItem> { new SelectListItem { Value = "Accept with Pleasure", Text = "Accept with Pleasure" }, new SelectListItem { Value = "Decline with Regret", Text = "Decline with Regret" } })</td>
</tr>
<tr>
<td><button class="btnSubmitRSVP" type="submit"> #(Model.EditableGuest.GuestID > 0? "Update": "Add") </button></td>
<td></td>
</tr>
</table>
</div>
</div>
}
We cannot figure out why the model is being passed and not the viewmodel? My code doesn't refer to the model? Any help would be much appreciated!
I'd say it's coming from here:
You're sending a GuestList in the else despite promising you'd send a GuestListViewModel at the top of the page:

Getting Checkbox Value and Text in Controller

Model
public class AllControls
{
public List<Group> getChkItems { get; set; }
public bool chk { get; set; }
}
public class Group
{
public int ID { get; set; }
public string Name { get; set; }
}
Controller:
[HttpGet]
public ActionResult Index()
{
List<Group> li = new List<Group>()
{
new Group() { ID = 1, Name = "C#" },
new Group() { ID = 1, Name = "Asp.NET" },
new Group() { ID = 1, Name = "SQL" }
};
AllControls model = new AllControls();
model.getChkItems = li;
return View(model);
}
[HttpPost]
public ActionResult Index(AllControls e)
{
return View(e);
}
View:
#using (Html.BeginForm())
{
foreach (var x in #Model.getChkItems)
{
#Html.CheckBoxFor(m => m.chk, new { value = #x.ID }) #x.Name
<br />
}
<input type="submit" value="Submit" id="btn" />
}
How can I get the selected checkbox value and text in the controller?
Here goes my solution. Let your model be as shown below.
public class CheckboxModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
public class MainModel
{
public List<CheckboxModel> CheckBoxes { get; set; }
}
And let your Controller GET Action be as shown below.
public ActionResult GetDatas()
{
MainModel model = new MainModel();
var list = new List<CheckboxModel>
{
new CheckboxModel{Id = 1, Name = "India", Checked = false},
new CheckboxModel{Id = 2, Name = "US", Checked = false},
new CheckboxModel{Id = 3, Name = "UK", Checked = false}
};
model.CheckBoxes = list;
return View(model);
}
And POST Action be as shown below.
[HttpPost]
public ActionResult PostDatas(MainModel model)
{
return View(model);
}
The View should be as shown below.
#model WebApplication1.Controllers.MainModel
#using (Html.BeginForm("PostDatas","Home"))
{
for (var i = 0; i < Model.CheckBoxes.Count; i++)
{
<table>
<tr>
<td>
#Html.HiddenFor(m => Model.CheckBoxes[i].Id)
#Html.HiddenFor(m => Model.CheckBoxes[i].Name)
#Html.CheckBoxFor(m => Model.CheckBoxes[i].Checked)
</td>
<td>
#Html.DisplayFor(m => Model.CheckBoxes[i].Name)
</td>
</tr>
</table>
}
<input id="submit" type="submit" value="submit" />
}
View will be rendered as shown below.
When you select India and US and click on submit button, you will get POST parameters as below.

Why is my form not populating my list on my view model?

When I build my form, my list populates correctly. However when I post back to my controller, the list SynonymTerm is null.
Here is my view model:
public class SynonymEditViewModel
{
public string Term { get; set; }
public List<SynonymTermEditViewModel> SynonymTerm;
public SynonymEditViewModel()
{
SynonymTerm = new List<SynonymTermEditViewModel>();
}
}
public class SynonymTermEditViewModel
{
public string Term { get; set; }
public string ReplacementTerm { get; set; }
public SynonymDuplicateWarning Warning { get; set; }
public SynonymTermEditViewModel()
{
Warning = new SynonymDuplicateWarning();
}
}
public class SynonymDuplicateWarning
{
public List<string> Terms { get; set; }
public SynonymDuplicateWarning()
{
Terms = new List<string>();
}
}
A simplified version of the view:
Edit.cshtml
#model MyProject.ViewModels.Synonyms.SynonymEditViewModel
<div class="form-group">
<div class="col-md-2">
#Html.LabelFor(model => model.Term)
</div>
<div class="col-md-10">
#Html.HiddenFor(model => model.Term)
#Html.DisplayFor(model => model.Term)
</div>
</div>
<table id="terms-table">
<tbody>
#Html.EditorFor(model => model.SynonymTerm)
</tbody>
</table>
EditorTemplates/SynonymTermEditViewModel.cshtml
#model MyProject.ViewModels.Synonyms.SynonymTermEditViewModel
<tr>
<td>
#Html.TextBoxFor(model => model.Term)
#Html.ValidationMessageFor(model => model.Term)
#Html.EditorFor(model => model.Warning)
</td>
</tr>
Here is the form data pulled from the browser request.
Term:Cat
SynonymTerm[0].Term:Feline
This data is passed to this controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(SynonymEditViewModel synonym)
{
if (ModelState.IsValid)
{
//breakpoint here
var dbSynonym = db.Synonym_Replacement_Term.Find(synonym.Term);
Mapper.Map(synonym, dbSynonym);
db.SaveChanges(User.Identity.Name, Request.ServerVariables["REMOTE_ADDR"]);
return RedirectToAction("Index");
}
return View(synonym);
}
The expected behavior is SynonymTerm is a list containing 1 object with Term="Feline", and all other properties having default/constructed values. Is there any reason SynonymTerm would be null?
I found the answer. In my ViewModel, I needed to add a getter/setter for the list:
public List<SynonymTermEditViewModel> SynonymTerm { get; set; }
Apparently they're required for the reflection used by MVC, according to this similar question.

Values for partial view model are null on submit

I cant successfully post the values from my partial view to my action - all the properties are null.
Partial View Model:
public class AddressViewModel
{
public string ClientNumber { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string Suburb { get; set; }
public string PostalCode { get; set; }
}
Partial View:
#model Models.AddressViewModel
#{
Layout = null;
}
#using (Html.BeginForm("UseAddress", "Home"))
{
<div>
<table>
<tr>
<td>
<div class="display-label">
#Html.DisplayNameFor(model => model.Line1)
</div>
</td>
<td>
<div class="display-field">
#Html.DisplayFor(model => model.Line1)
</div>
</td>
........
</tr>
</table>
<input type="submit" name="UseAddress" id="submitbutton" value="Use Address" />
</div>
}
Action:
[HttpPost]
[Authorize]
public ActionResult UseAddress(AddressViewModel model)
{
return RedirectToAction("Index", "Home");
}
The partial view is rendered on the page by selecting a dropdown as follows:
<script type="text/javascript">
$(function () {
$('#AddressTypeDropdownList').change(function () {
var url = $(this).data('url');
$('#Address').load(url);
});
});
</script>
#Html.DropDownListFor(
x => x.SelectedAddressTypeId,
new SelectList(Model.AddressTypes, "Value", "Text"),
"-- Select Address Type --",
new
{
id = "AddressTypeDropdownList",
data_url = Url.Action("_Address", "Home")
}
)
<div id="Address"></div>
public ActionResult _Address()
{
AddressViewModel addressViewModel = new AddressViewModel {
ClientNumber = "test"
};
return PartialView(addressViewModel);
}
I would expect the UseAddress method to have the model.ClientNumber == "test" when I click the submit button but it is null...Is there anything obvious that I'm doing wrong?
DisplayFor doesn't create input for the field so it won't get posted. You'll need to add
#Html.HiddenFor(model => model.Line1)
....
To post the values.

The model item passed into the dictionary is of type 'yyyy', but this dictionary requires a model item of type 'xx'

I have two model and I want to show in one view. So I'm using
#Html.Partial
This is my first Model.
public partial class graduandModel :BaseNopEntityModel
{
public graduandModel()
{
this.AvailableCeremony = new List<SelectListItem>();
}
public string first_name { get; set; }
public string middle_name { get; set; }
public string last_name { get; set; }
public int student_id { get; set; }
public int ceremony_id { get; set; }
public DateTime ceremony_date { get; set; }
public int graduand_id { get; set; }
public IList<SelectListItem> AvailableCeremony { get; set; }
public graduandDegreeModel graduandDegreeGroup { get; set; }
}
This is my second Model.
public class graduandDegreeModel
{
public graduandDegreeModel()
{
this.AvailableDegree = new List<SelectListItem>();
}
public string degree_id { get; set; }
public int graduand_id { get; set; }
public string degree_name { get; set; }
public IList<SelectListItem> AvailableDegree { get; set; }
}
This is mu controller
public ActionResult CheckData(int ceremony_id, string first_name, string middle_name, string last_name)
{
graduandModel model = new graduandModel();
graduandDegreeModel model_1 = new graduandDegreeModel();
var graduandList = _graduandService.GetGraduandByStudent(ceremony_id, first_name, middle_name, last_name);
if (graduandList.Count != 0)
{
model.ceremony_id = ceremony_id;
model.first_name = first_name;
model.middle_name = middle_name;
model.last_name = last_name;
// var degreeList = "";
foreach (var c in graduandList)
{
var degreeList = _graduandDegreeService.getAllDegreeIdBtGraduandId(c.graduand_id);
foreach (var d in degreeList)
{
model_1.AvailableDegree.Add(new SelectListItem() { Text = d.Degree.degree_name, Value = d.degree_id });
}
}
}
return View(model);
}
This is my views
#{
Layout = "~/Views/Shared/_ColumnsThree.cshtml";
}
#model graduandModel
#using Nop.Web.Models.Hire;
#using Nop.Web.Framework;
#using Telerik.Web.Mvc.UI;
#using System.Linq;
#using (Html.BeginForm())
{
<table >
<tr>
<td >
Ceremony :
</td>
<td>
Ceremony at #Model.ceremony_date
</td>
</tr>
<tr>
<td >
Name :
</td>
<td >
#Model.first_name #Model.middle_name #Model.last_name
</td>
</tr>
</table>
<div>
#Html.Partial("_DegreeDetailsByGraduand", Model.graduandDegreeGroup)
</div>
}
This is my Partial view
#{
Layout = "~/Views/Shared/_ColumnsThree.cshtml";
}
#model graduandDegreeModel
#using Nop.Web.Models.Hire;
#using Nop.Web.Framework;
#using Telerik.Web.Mvc.UI;
#using System.Linq;
<table >
<tr>
<td >
AAAAAA
</td>
<td>
#Html.DropDownListFor(model => model.degree_id, Model.AvailableDegree)
#* #Html.ValidationMessageFor(model => model.ceremony_id)*#
</td>
</tr>
</table>
there is error
The model item passed into the dictionary is of type 'Nop.Web.Models.Hire.graduandModel', but this dictionary requires a model item of type 'Nop.Web.Models.Hire.graduandDegreeModel'.
How can I slove it???
You didn't create an instance for graduandModel's graduandDegreeGroup property. So this line:
#Html.Partial("_DegreeDetailsByGraduand", Model.graduandDegreeGroup)
will throw an exception like you said. Simply because the second parameter is NULL.
You can try to modify graduandModel's constructor as below:
public graduandModel()
{
this.AvailableCeremony = new List<SelectListItem>();
this.graduandDegreeGroup = new graduandDegreeModel();
}
The exception should be gone.
You may also find this link helpful: ASP.NET MVC renderpartial, model item passed into the dictionary is of type
Another option for you may be to create a new view model which combines the two models above into one. That way it has properties for all of the data you require for this view. Then you don't need to specify a model in your call to the partial view, it will automatically use the parent's model. Alternatively, you may not need to separate the view into partials at all with the use of a combined model. It is not uncommon to have a unique view model for each different view. In some applications, it can be rare that two different views require the same data.
The combined view model:
public class CheckDataViewModel
{
public CheckDataViewModel ()
{
this.AvailableCeremony = new List<SelectListItem>();
this.AvailableDegree = new List<SelectListItem>();
}
public string first_name { get; set; }
public string middle_name { get; set; }
public string last_name { get; set; }
public int student_id { get; set; }
public int ceremony_id { get; set; }
public DateTime ceremony_date { get; set; }
public int graduand_id { get; set; }
public IList<SelectListItem> AvailableCeremony { get; set; }
public graduandDegreeModel graduandDegreeGroup { get; set; }
public string degree_id { get; set; }
public string degree_name { get; set; }
public IList<SelectListItem> AvailableDegree { get; set; }
}
The combined view:
#{
Layout = "~/Views/Shared/_ColumnsThree.cshtml";
}
#model CheckDataViewModel
#using Nop.Web.Models.Hire;
#using Nop.Web.Framework;
#using Telerik.Web.Mvc.UI;
#using System.Linq;
#using (Html.BeginForm())
{
<table >
<tr>
<td >
Ceremony :
</td>
<td>
Ceremony at #Model.ceremony_date
</td>
</tr>
<tr>
<td >
Name :
</td>
<td >
#Model.first_name #Model.middle_name #Model.last_name
</td>
</tr>
</table>
<div>
<table >
<tr>
<td >
AAAAAA
</td>
<td>
#Html.DropDownListFor(model => model.degree_id, Model.AvailableDegree)
#* #Html.ValidationMessageFor(model => model.ceremony_id)*#
</td>
</tr>
</table>
</div>
}

Categories

Resources