I m using a viewmodel on my create view. Every thing works but on form post via jquery I m getting The Id field is required ModelState error. I have seen some solutions regarding adding [Bind(Exclude = "Id")] annotation in the model class but then when I call the same action method on the Update model it never binds the Id of the model and inserts a new record in db.
My viewmodel looks like
public class MemberSiteContactModel
{
public int Id { get; set; }
[Display(Name = "Name")]
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
// More items
}
My partial view looks like
#model MemberSiteContactModel
<tr class="highlight">
<td class="col-sm-1">#Html.TextBoxFor(x => x.Name, new { #class = "name" })</td>
<td class="col-sm-2">#Html.TextBoxFor(x => x.ContactNo, new { #class = "contactNo" })</td>
<td class="col-sm-1"><input type="button" class="btn btn-xs btn-success contactSaveRow" value="Save" /></td>
<td class="col-sm-1"><input type="button" class="btn btn-xs btn-danger contactDeleteRow" value="Remove" name="btnRemoveContact" /></td>
</tr>
My controller looks like
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SaveSiteContact(MemberSiteContactModel memberSiteContactModel)
{
if (ModelState.IsValid)
{
//processing
}
}
I have also created a sample application to replicate the behavior
public class CityModel
{
public int Id { get; set; }
[Display(Name = "City")]
[Required(ErrorMessage = "City is required")]
public string City { get; set; }
}
public ActionResult Contact()
{
return View();
}
#model WebApplication3.Models.CityModel
#using (#Html.BeginForm("SaveCity", "Home", FormMethod.Post))
{
#Html.HiddenFor(x => x.Id)
#Html.TextBoxFor(x => x.City)
<input type="submit" value="Submit" />
}
[HttpPost]
public ActionResult SaveCity(CityModel cityModel)
{
if (ModelState.IsValid)
{
}
return null;
}
You're not passing the Id in, you need to add a hidden field that holds to Id otherwise how does the Id ever get passed into the posted model.
#Html.HiddenFor(x => x.Id)
I'm using MVC and have the same problem, so i check out all my create code and find that i wasn't send an empty model to the View at call time. The solution was simple; on the Create() action result, just add a line that creates a new ViewModel and set it as parameter to the View or Partial View return. Something like this:
public ActionResult Create(){
ViewModel entity = new ViewModel();
return PartialView(entity);
}
I hope that helps.
Related
I am having trouble binding a model that contains list of objects for Editing method. This is the list of Factory which includes list of another object (FactoryHotline).
There is no problem when I get pass data from Controller to View. But when I try to send data from View back to Controller, some model's properties always null.
The Model is:
public class Factory
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<FactoryHotline> FactoryHotlineList { get; set; }
}
public class FactoryHotline
{
public Guid Id { get; set; }
public Guid FactoryId { get; set; }
public string Caption { get; set; }
public string Hotline { get; set; }
}
This is View:
#model List<WebDataLayer.Models.Factory>
<form action="/Factories/Edit" method="POST" enctype="multipart/form-data">
#Html.AntiForgeryToken()
<div class="form-horizontal">
<table id="factoriesTable">
<thead>
<tr>
<th>Name</th>
<th class="Hotline1" >Hotline 1</th>
<th class="Hotline2" >Hotline 2</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(model => model[i].Id)
<tr>
<td>#Model[i].Name</td>
#for (int h = 0; h < Model[i].FactoryHotlineList.Count; h++)
{
<td>
<div>
<b>Caption: </b>
#Html.EditorFor(model => model[i].FactoryHotlineList[h].Caption, new { htmlAttributes = new { #class = "form-control ShortInput", id = "captionInput", maxlength = "39" } })
</div>
<div>
<b>Hotline:</b>
#Html.EditorFor(model => model[i].FactoryHotlineList[h].Hotline, new { htmlAttributes = new { #class = "form-control ShortInput", id = "hotlineInput", maxlength = "15" } })
#Html.ValidationMessageFor(model => model[i].FactoryHotlineList[h].Hotline)
</div>
</td>
}
</tr>
}
</tbody>
</table>
</form>
In my controller the method for Edit is:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit (List<Factory> factories)
{
}
Only Id has value, the other(Caption, Hotline) are always null in List<Factory> factories
This is how I am passing Data from Controller to View
// GET: Edit
public ActionResult Edit()
{
var factories = _factoryService.All().OrderBy(p => p.Name);
var list = factories.ToList();
return View("Edit", list);
}
I works fine using Entity Framework.
That is because you have used HiddenFor to keep id as hidden field. To have the value in postback, it should be a part of input element(input,select,checkbox,textarea,etc) or as hidden field.
#Html.HiddenFor(model => model[i].Name)
I would suggest using a viewmodel along with automapper in this case.
Im working on an application which can download emails. In my view if checkbox/checkboxes are checked, i want to pass an ID to controller to download this messages. I will try to explain with code:
#using (Html.BeginForm("DownloadData", "Messages", FormMethod.Post))
{
<table class="table table-bordered table-hover">
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td><input type="checkbox"
name="selectedIds[#i].messageID"/></td>
<td>#Model[i].messageFrom</td>
<td>#Model[i].messageSubject</td>
<td>#Model[i].messageDate</td>
</tr>
}
</table>
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Download" />
</div>
}
The #i in name="selectedIds[#i].messageID" doesnt going to be good because its increasing and if i check: 1st the 2nd and the 4th message, it will add only the first two elements to my list.
The controller:
[HttpPost]
public ActionResult DownloadData(List<MessagesModels> selectedIds)
{
return View(...);
}
The model:
public class MessagesModels
{
public int messageID { get; set; }
public string messageSubject { get; set; }
...
}
I think it isnt hard, but cant find the sollution. Thanks for those who can help!
To avoid changing your message model just for this view (or if you are unable to begin with) I would recommend creating a new model for this view to handle selecting like this:
public class MessageSelectionViewModel
{
public MessagesModels Message { get; set; }
public bool IsSelected { get; set; }
}
Your view would now be a list of these new view objects so would look more like this:
<td>#Html.CheckBoxFor(item => item[i].IsSelected)</td>
<td style="display:none;">#Html.HiddenFor(item => item[i].Message.Id)</td>
<td>#Model[i].Message.messageFrom</td>
<td>#Model[i].Message.messageSubject</td>
<td>#Model[i].Message.messageDate</td>
Your post method would become:
[HttpPost]
public ActionResult DownloadData(IEnumerable<MessageSelectionViewModel> selectedMessages)
{
foreach (int messageId in selectedMessages.Where(m => m.IsSelected == true).Select(m => m.Message.Id))
{
}
return View(...);
}
Of course if it's not a big deal you could save some time and effort and just add IsSelected to your model like Stephen suggested.
I have a FeedbackViewModel that contains a list of questions:
public class FeedbackViewModel
{
public List<QuestionViewModel> Questions { get; set; }
}
This QuestionViewModel is an object that can be inherited by 5 different types of questions
public class QuestionViewModel
{
public string QuestionText { get; set; }
public string QuestionType { get; set; }
}
An example of one of the inheriting question types:
public class SingleQuestionViewModel : QuestionViewModel
{
public string AnswerText { get; set; }
}
In the HttpGet of the Index action in the controller I get the questions from the database and add the correct question type in list of question in the FeedbackViewModel Then I render this model in the view:
#using (Html.BeginForm())
{
//foreach (var item in Model.Questions)
for (int i = 0; i < Model.Questions.Count; i++)
{
<div class="form-group">
#Html.DisplayFor(modelItem => Model.Questions[i].QuestionText, new { #class = "control-label col-md-4" })
<div class="col-md-6">
#if (Model.Questions[i].QuestionType == "Single")
{
#Html.EditorFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText)
}
else if (Model.Questions[i].QuestionType == "Multiple")
{
#Html.TextAreaFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText)
}
else if (Model.Questions[i].QuestionType == "SingleSelection")
{
#Html.RadioButtonForSelectList(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleSelectionQuestionViewModel).SelectedAnswer,
(Model.Questions[i] as OpenDataPortal.ViewModels.SingleSelectionQuestionViewModel).SelectionAnswers)
}
else if (Model.Questions[i].QuestionType == "MultipleSelection")
{
#Html.CustomCheckBoxList((Model.Questions[i] as OpenDataPortal.ViewModels.MultipleSelectionQuestionViewModel).AvailableAnswers)
}
else if (Model.Questions[i].QuestionType == "UrlReferrer")
{
#Html.EditorFor(modelItem => (Model.Questions[i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText)
}
</div>
</div>
<br />
}
<br />
<button type="submit">Submit</button>
}
Now, I simply can't get it to post the list of questions in the model. Is it even possible to post a list of different object types?
Edit: Following is the list of data within the post that I discovered using Fiddler:
After much research I've found two solutions:
One is to write HTML that has hardcoded Id's and Names
Two is to convert your ICollection/IEnumerable to an Array or List (i.e IList something with an 'index'), and have an Array object in your BindingModel in your Controller POST Action.
Thanks to Phil Haack's (#haacked) 2008 blog post http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/
Which is still relevant to how the default ModelBinder works today for MVC.
(NB: the links in Phil's article to sample porject and extension methods are broken)
HTML snippet that inspired me:
<form method="post" action="/Home/Create">
<input type="hidden" name="products.Index" value="cold" />
<input type="text" name="products[cold].Name" value="Beer" />
<input type="text" name="products[cold].Price" value="7.32" />
<input type="hidden" name="products.Index" value="123" />
<input type="text" name="products[123].Name" value="Chips" />
<input type="text" name="products[123].Price" value="2.23" />
<input type="submit" />
</form>
Post array looks a bit like:
products.Index=cold&products[cold].Name=Beer&products[cold].Price=7.32&products.Index=123&products[123].Name=Chips&products[123].Price=2.23
Model:
public class CreditorViewModel
{
public CreditorViewModel()
{
this.Claims = new HashSet<CreditorClaimViewModel>();
}
[Key]
public int CreditorId { get; set; }
public string Comments { get; set; }
public ICollection<CreditorClaimViewModel> Claims { get; set; }
public CreditorClaimViewModel[] ClaimsArray {
get { return Claims.ToArray(); }
}
}
public class CreditorClaimViewModel
{
[Key]
public int CreditorClaimId { get; set; }
public string CreditorClaimType { get; set; }
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:N2}")]
public Decimal ClaimedTotalAmount { get; set; }
}
Controller GET:
public async Task<ActionResult> Edit(int id)
{
var testmodel = new CreditorViewModel
{
CreditorId = 1,
Comments = "test",
Claims = new HashSet<CreditorClaimViewModel>{
new CreditorClaimViewModel{ CreditorClaimId=1, CreditorClaimType="1", ClaimedTotalAmount=0.00M},
new CreditorClaimViewModel{ CreditorClaimId=2, CreditorClaimType="2", ClaimedTotalAmount=0.00M},
}
};
return View(model);
}
Edit.cshtml:
#Html.DisplayNameFor(m => m.Comments)
#Html.EditorFor(m => m.Comments)
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(m => Model.Claims.FirstOrDefault().CreditorClaimType)
</th>
<th>
#Html.DisplayNameFor(m => Model.Claims.FirstOrDefault().ClaimedTotalAmount)
</th>
</tr>
<!--Option One-->
#foreach (var item in Model.Claims)
{
var fieldPrefix = string.Format("{0}[{1}].", "Claims", item.CreditorClaimId);
<tr>
<td>
#Html.DisplayFor(m => item.CreditorClaimType)
</td>
<td>
#Html.TextBox(fieldPrefix + "ClaimedTotalAmount", item.ClaimedTotalAmount.ToString("F"),
new
{
#class = "text-box single-line",
data_val = "true",
data_val_number = "The field ClaimedTotalAmount must be a number.",
data_val_required = "The ClaimedTotalAmount field is required."
})
#Html.Hidden(name: "Claims.index", value: item.CreditorClaimId, htmlAttributes: null)
#Html.Hidden(name: fieldPrefix + "CreditorClaimId", value: item.CreditorClaimId, htmlAttributes: null)
</td>
</tr>
}
</table>
<!--Option Two-->
#for (var itemCnt = 0; itemCnt < Model.ClaimsArray.Count(); itemCnt++)
{
<tr>
<td></td>
<td>
#Html.TextBoxFor(m => Model.ClaimsArray[itemCnt].ClaimedTotalAmount)
#Html.HiddenFor(m => Model.ClaimsArray[itemCnt].CreditorClaimId)
</td></tr>
}
Form is processed in the Controller:
Post Model:
public class CreditorPostViewModel
{
public int CreditorId { get; set; }
public string Comments { get; set; }
public ICollection<CreditorClaimPostViewModel> Claims { get; set; }
public CreditorClaimPostViewModel[] ClaimsArray { get; set; }
}
public class CreditorClaimPostViewModel
{
public int CreditorClaimId { get; set; }
public Decimal ClaimedTotalAmount { get; set; }
}
Controller:
[HttpPost]
public ActionResult Edit(int id, CreditorPostViewModel creditorVm)
{
//...
Make sure you are rendering your view in order so that Model.Questions[i] renders in order.
For example, Model.Questions[0], Model.Questions[1], Model.Questions[2].
I noticed that if the order is not correct mvc model binder will only bind the first element.
Thanks for pointing me in the right direction with this post. I was struggling to get the syntax right for binding a non-sequential IDictionary<string, bool> object. Not sure this is 100% correct, but this Razor code worked for me:
<input type="hidden" name="MyDictionary.Index" value="ABC" />
<input type="hidden" name="MyDictionary[ABC].Key" value="ABC" />
#Html.CheckBox(name: "MyDictionary[ABC].Value", isChecked: Model.MyDictionary["ABC"], htmlAttributes: null)
If you need a checkbox, be sure to use Html.CheckBox instead of a standard HTML checkbox. The model will blow up if a value is not provided, and Html.CheckBox generates a hidden field to ensure a value is present when the checkbox is not checked.
Using Razor you can implement the for loop using a dictionary as follows without making changes to your object:
#foreach (var x in Model.Questions.Select((value,i)=>new { i, value }))
{
if (Model.Questions[x.i].QuestionType == "Single")
{
#Html.EditorFor(modelItem => (modelItem.Questions[x.i] as OpenDataPortal.ViewModels.SingleQuestionViewModel).AnswerText)
}
...
}
The collection needs to be either a List or Array for this to work.
I use this code maybe its can help
<input type="hidden" name="OffersCampaignDale[#(item.ID)].ID" value="#(item.ID)" />
#Html.Raw(Html.EditorFor(modelItem => item.NameDale, new { htmlAttributes = new { #class = "form-control" } })
.ToString().Replace("item.NameDale", "OffersCampaignDale[" + item.ID+ "].NameDale").Replace("item_NameDale", "NameDale-" + item.ID))
#Html.ValidationMessageFor(modelItem => item.NameDale, "", new { #class = "text-danger" })
VS'12 KendoUI InternetApplication Template C# asp.net EF Code First
My Question is how to pass both the Regular ( are passing now ) values and the Ienumerable(passing null) into my controller and saving them to the Database using EF Code First in a Many-2-Many Relationship manor.
The Following is what i have tried
Main View
#model OG.Models.UserProfiles
#using (Html.BeginForm())
{
<div class="editor-field">
<div class="Containter">
<div>
#Html.DisplayFor(model => model.UserName)
</div>
<div class="contentContainer">
#foreach (var item in Model.Prospects)
{
<table>
<tr>
<td>
#Html.Label("Current Prospects")
</td>
</tr>
<tr>
<td>
#Html.DisplayNameFor(x=>item.ProspectName)
</td>
</tr>
</table>
}
</div>
</div>
<div class="contentContainer2">
#Html.Partial("_UsersInProspectsDDL", new OG.ModelView.ViewModelUserInProspects() { Users = Model.UserName })
</div>
</div>
}
Partial View
#model OG.ModelView.ViewModelUserInProspects
<label for="prospects">Prospect:</label>
#(Html.Kendo().DropDownListFor(m=>m.Prospects)
.Name("Prospects")
.HtmlAttributes(new { style = "width:300px"}) //, id = "countys"})
.OptionLabel("Select Prospect...")
.DataTextField("ProspectName")
.DataValueField("ProspectID")
.DataSource(source => {
source.Read(read =>
{
read.Action("GetCascadeProspects", "ChangeUsersInfo")
.Data("filterProspects");
})
.ServerFiltering(true);
})
.Enable(false)
.AutoBind(false)
.CascadeFrom("Clients")
</div>
Model for PartialView
public class ViewModelUserInProspects
{
public string Clients { get; set; }
public IEnumerable<dbClient> AvailableClients { get; set; }
public string Prospects { get; set; }
public IEnumerable<dbProspect> AvailableProspects { get; set; }
public string Users { get; set; }
public IEnumerable<UserProfiles> AvailableUsers {get;set;}
}
}
Main Model
Standart SimpleMemberShipUserTable
Post Method
[HttpPost]
public ActionResult UsersInProspect(
[Bind(Include= "ProspectName, ProspectID")]
UserProfiles userprofiles, ViewModelUserInProspects values, FormCollection form)
//<- Trying different things sofar
{
if (ModelState.IsValid)
{
//string something = form["Prospects"];
int prosID = Convert.ToInt16(values.Prospects);
int UserID = userprofiles.UserID; // <- THIS VALUE is null atm.
This is where i need to save both ID's to the EF Generated / Mapped Table. Unsure how.
db.Entry(userprofiles).CurrentValues.SetValues(userprofiles);
db.Entry(userprofiles).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(userprofiles);
}
Please take a look Here
Goes over ViewModels
What EditorTemplate are and how to use them
What the GET Method would look like
What the Edit View would look like
Give you a View Example
What the Post Method would look like
I've been reading the various posts on view models and check boxes, but my brain is starting to lock up and I need a little push in the right direction.
Here's my simplified view model. I have checkboxes that need to populate the lists with their values. I don't think this can happen automagically. I'm not sure how to bridge the gap between an array of string values and a List correctly. Suggestions?
public int AlertId { get; set; }
public List<int> UserChannelIds { get; set; }
public List<int> SharedChannelIds { get; set; }
public List<int> SelectedDays { get; set; }
Have your View Model like this to represent the CheckBox item
public class ChannelViewModel
{
public string Name { set;get;}
public int Id { set;get;}
public bool IsSelected { set;get;}
}
Now your main ViewModel will be like this
public class AlertViewModel
{
public int AlertId { get; set; }
public List<ChannelViewModel> UserChannelIds { get; set; }
//Other Properties also her
public AlertViewModel()
{
UserChannelIds=new List<ChannelViewModel>();
}
}
Now in your GET Action, you will fill the values of the ViewModel and sent it to the view.
public ActionResult AddAlert()
{
var vm = new ChannelViewModel();
//The below code is hardcoded for demo. you mat replace with DB data.
vm.UserChannelIds.Add(new ChannelViewModel{ Name = "Test1" , Id=1});
vm.UserChannelIds.Add(new ChannelViewModel{ Name = "Test2", Id=2 });
return View(vm);
}
Now Let's create an EditorTemplate. Go to Views/YourControllerName and Crete a Folder called "EditorTemplates" and Create a new View there with the same name as of the Property Name(ChannelViewModel.cshtml)
Add this code ro your new editor template.
#model ChannelViewModel
<p>
<b>#Model.Name</b> :
#Html.CheckBoxFor(x => x.IsSelected) <br />
#Html.HiddenFor(x=>x.Id)
</p>
Now in your Main View, Call your Editor template using the EditorFor Html Helper method.
#model AlertViewModel
<h2>AddTag</h2>
#using (Html.BeginForm())
{
<div>
#Html.LabelFor(m => m.AlertId)
#Html.TextBoxFor(m => m.AlertId)
</div>
<div>
#Html.EditorFor(m=>m.UserChannelIds)
</div>
<input type="submit" value="Submit" />
}
Now when You Post the Form, Your Model will have the UserChannelIds Collection where the Selected Checkboxes will be having a True value for the IsSelected Property.
[HttpPost]
public ActionResult AddAlert(AlertViewModel model)
{
if(ModelState.IsValid)
{
//Check for model.UserChannelIds collection and Each items
// IsSelected property value.
//Save and Redirect(PRG pattern)
}
return View(model);
}
Part of My View Model:
public List<int> UserChannelIds { get; set; }
public List<int> SharedChannelIds { get; set; }
public List<int> Weekdays { get; set; }
public MyViewModel()
{
UserChannelIds = new List<int>();
SharedChannelIds = new List<int>();
Weekdays = new List<int>();
}
I used partial views to display my reusable checkboxes (I didn't know about editor templates at this point):
#using AlertsProcessor
#using WngAlertingPortal.Code
#model List<int>
#{
var sChannels = new List<uv_SharedChannels>();
Utility.LoadSharedChannels(sChannels);
}
<p><strong>Shared Channels:</strong></p>
<ul class="channel-list">
#{
foreach (var c in sChannels)
{
string chk = (Model.Contains(c.SharedChannelId)) ? "checked=\"checked\"" : "";
<li><input type="checkbox" name="SharedChannelIds" value="#c.SharedChannelId" #chk /> #c.Description (#c.Channel)</li>
}
}
All three checkbox partial views are similar to each other. The values of the checkboxes are integers, so by lining up my view model List names with the checkbox names, the binding works.
Because I am working in int values, I don't feel like I need the extra class to represent the checkboxes. Only checked checkboxes get sent, so I don't need to verify they are checked; I just want the sent values. By initializing the List in the constructor, I should be avoiding null exceptions.
Is this better, worse or just as good as the other solution? Is the other solution (involving an extra class) best practice?
The following articles were helpful to me:
http://forums.asp.net/t/1779915.aspx/1?Checkbox+in+MVC3
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Binding list with view model
This site handles it very nicely
https://www.exceptionnotfound.net/simple-checkboxlist-in-asp-net-mvc/
public class AddMovieVM
{
[DisplayName("Title: ")]
public string Title { get; set; }
public List<CheckBoxListItem> Genres { get; set; }
public AddMovieVM()
{
Genres = new List<CheckBoxListItem>();
}
}
public class MembershipViewData
{
public MembershipViewData()
{
GroupedRoles = new List<GroupedRoles>();
RolesToPurchase = new List<uint>();
}
public IList<GroupedRoles> GroupedRoles { get; set; }
public IList<uint> RolesToPurchase { get; set; }
}
//view
#model VCNRS.Web.MVC.Models.MembershipViewData
#{
ViewBag.Title = "MembershipViewData";
Layout = "~/Views/Shared/_Layout.cshtml";
int i = 0;
}
#using (Html.BeginForm("Membership", "Account", FormMethod.Post, new { id = "membershipForm" }))
{
<div class="dyndata" style="clear: left;">
<table width="100%" cellpadding="0" cellspacing="0" class="table-view list-view">
foreach (var kvp2 in Model.GroupedRoles)
{
string checkBoxId = "RolesToPurchase" + kvp2.RoleType;
<tr>
<td width="240px">
<label class="checkbox-label" for="#checkBoxId">
<input type="checkbox" class="checkbox" name="RolesToPurchase[#i]"
id="#checkBoxId" value="#kvp2.RoleType" />
#kvp2.Key
</label>
</td>
</tr>
i++;
}
<tr style="background-color: #ededed; height: 15px;">
<td colspan="5" style="text-align: right; vertical-align: bottom;">
#Html.SubmitButton(Resources.MyStrings.Views_Account_Next)
</td>
</tr>
</table>
</div>
}
//Post Action
[HttpPost]
public ActionResult Membership(MembershipViewData viewData)
{
..........................
}
}