On an ASP.NET MVC 5 project I have the following model:
public class ScheduleIndexModel {
public IPageList<DataModel> Data { get; set; }
public FormModel Form { get; set; }
public class DataModel {
public Int32 Id { get; set; }
public String[] Attendees { get; set; }
public String Location { get; set; }
public DateTime Date { get; set; }
} // DataModel
public class FormModel {
public String Location { get; set; }
public String Date { get; set; }
} // FormModel
}
The view is the following:
<form action="#Url.Action(MVC.Schedule.Index())" method="post">
#Html.LabelFor(x => x.Form.Date, "Date")
#Html.TextBoxFor(x => x.Form.Date)
#Html.LabelFor(x => x.Form.Location, "Location")
#Html.TextBoxFor(x => x.Form.Location)
#Html.SubmitButton("Filter", "Index", new { #class = "submit" })
#Html.AntiForgeryToken()
</form>
Then the HTTPPost controller action is as follows:
[HttpPost]
public virtual ActionResult Index(ScheduleIndexModel.FormModel model, Int32 p = 1) {
return View();
} // Index
When I submit the form the model is not null but its properties are even if I write something on the TextBoxes.
Does anyone knows what am I doing wrong?
You may need to use a binding prefix because your viewmodel is nested. Something like this may work:
public virtual ActionResult Index([Bind(Prefix = "Form")] FormModel model)
Your html helpers (e.g. #Html.TextBoxFor(x => x.Form.Date) will be generating html like this
<input name="Form.Date" .../>
but because your post method accepts parameter of type FormModel it cant match up (FormModel only has property Date, not property Form that has property Date).
You can either change you action method to
public virtual ActionResult Index(ScheduleIndexModel model,...
{
FormModel form = model.Form; // get the FormModel
or use the [Bind(Prefix..)] as suggested by Big Daddy
Related
we have some c# model
public class PartnerRegistrationForm
{
public string Name { get; set; }
public string Company { get; set; }
public string Email { get; set; }
}
class that contains this model
public class PartnerRegistrationFormHolder
{
public PartnerRegistrationForm PartnerRegistrationForm { get; set; }
}
view
#model WebApplication1.Models.PartnerRegistrationFormHolder
#using (Html.BeginForm("Index", "Registration", FormMethod.Post))
{
#Html.TextBoxFor(e => e.PartnerRegistrationForm.Name)
#Html.TextBoxFor(e => e.PartnerRegistrationForm.Email)
#Html.TextBoxFor(e => e.PartnerRegistrationForm.Company)
<button type="submit">Send</button>
}
this method TextBoxFor create inputs with long names like 'PartnerRegistrationForm.Company'
ok, its reflection magic
then i fill this form and submit it
i have view
[HttpPost]
public ActionResult Index(PartnerRegistrationFormHolder partnerRegistrationFormHolder)
{
return new HttpNotFoundResult();
}
i run my program with debug and take a breakpoint on this action
HOW MVC create object from form? can anyone explain me or give me some link where to read?
more reflection magic :) it's called model binding:
https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-2.1
I am developing a simple mvc application . The code is as follows:
Model .cs:
public class CustomModel
{
public IEnumerable<lang> lstlang { get; set; }
public IEnumerable<org> lstOrg { get; set; }
}
public class lang
{
public int langid { get; set; }
public string langName { get; set; }
}
public class org
{
public int orgId { get ;set;}
public string orgName { get; set; }
}
Controller.cs
public Action Index()
{
// Get data from database and fill the model
var model = new CustomModel();
return View(model);
}
public Action Partial()
{
// Get data from database and fill the model
var model = new CustomModel();
return PartialView(model);
}
[HttpPost]
public Action Partial(FormCollection frm, CustomModel model)
{
// Get data from database and fill the model
var model = new CustomModel();
return PartialView(model);
}
Index.cshtml
#model CustomModel
#Html.TextboxFor(x => x.lang.FirstOrDefault().id);
<input type="button" id="btn" />
#Html.RenderPartial("Partial", model)
Partial.cshtml
#model CustomModel
#Html.TextboxFor(x => x.lang.FirstOrDefault().id);
<input type="submit" id="submit" />
The thing is, when I click the submit button in the Partial.cshtml page, and examine the model in httppost method in public Action Partial(FormCollection frm, CustomModel model), the model contains null for both lists lstlang and lstOrg, but the formcollection[0] will give the selected textbox value.
What am I missing, or is this the right way of using partial views?
Don't use FirstOrDefault(). If you want to post something back to the front end with collections, you'll need to use indexing.
Public class CustomModel
{
public ICollection<lang> lstlang { get; set; }
public ICollection<org> lstOrg { get; set; }
}
#HTML.textboxfor(x=>x.lang[0].id);
i am trying to get the value of selected check-box using model but not able to get as i want ;
Below is the table image
below is my code for this VIEW
And below is the code for result.I get null value
And below is my model declaration
public class RoleDetail
{
[Key]
public int RoleDetailID { get; set; }
public bool IsCreate { get; set; }
public bool IsDelete { get; set; }
public bool IsView { get; set; }
public bool IsEdit { get; set; }
public bool IsDownload { get; set; }
public string ControllerName { get; set; }
public System.DateTime CreateDate { get; set; }
public Int32 UserTypeId { get; set; }
}
public enum ControllerName
{
Account, Candidate, Career, ChooseUs, ContactUs, DocumentType, Employee, Gallery, GalleryType, GetTouch, Home, JobCategory, Jobs, Portfolio, ResumeUpload, RoleDetail, Services, User, UserRoleType
}
Replace the foreach loop in your view with a for:
#for (var i = 0; i < lst.Count; i++)
{
...
#Html.CheckBoxFor(x => lst[i].IsCreate)
#Html.CheckBoxFor(x => lst[i].IsView)
#Html.CheckBoxFor(x => lst[i].IsDelete)
...
}
For this to work make sure that the variable you are iterating over is an IList<T> or T[].
Also your controller action argument should be named accordingly:
public ActionResult Create(IEnumerable<RoleDetail> lst)
{
...
}
You should not be creating RoleDetail in the view. In the GET method create a List<RoleDetail>, populate it with the objects you want to display and return it to the view.
Controller
public ActionResult Create()
{
List<RoleDetail> model = new List<RoleDetail>();
// populate the collection, for example
foreach(var name in Enum.GetNames(typeof(ControllerName)))
{
model.Add(new RoleDetail()
{
ControllerName = name,
IsCreate = true // etc
});
}
return View(model);
}
public ActionResult Create(IEnumerable<RoleDetail> model)
{
}
View
#model List<RoleDetail>
#using(Html.BeginForm())
{
for(int i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(m => m.ControllerName) // needed for postback
#Html.DisplayFor( => m.ControllerName)
#Html.CheckBoxFor(m => m.IsCreate)
....
}
<input type="submit" />
}
Side notes
Do not try to override the name (or value) attribute. The html
helper set these correctly for model binding (and in any case you
were only setting it to the value the helper generated anyway)
The reason the foreach loop does not work is your generating
duplicate name attributes (and also invalid html due to duplcate
id attributes). The for loop correctly generates the correct
names with indexers (e.g. <input name="[0].IsCreate " ..>, <input
name="[1].IsCreate " ..> etc.
You don't appear to be rendering controls for all of you model
properties so use a view model containing only those properties you
need to display/edit. What is a view model in MVC
You have public enum ControllerName so I suspect property
ControllerName in RoleDetail should be public ControllerName ControllerName { get; set; }?
And in future, post the code, not an image of it!
Can't get all my ViewModel properties back after the postback (After user entered some values on HttpPost)
There are numerous questions here related to losing data or getting nulls after the postback
I tried some of them and played around on my case, I think the scenario is a bit different,
Using a PartialView or Editor Templates(except a list property), Always the returned result properties are null.
In partialView approach always all the properties are null, I think maybe I missed a piece.
In the "custom editor template approach for the type", I'll have just "EnteredNums" List returned. (Maybe because these are what the template have EditorFor for them, but what is the solution here if that's the case?)
Don't know weather if it's important here or not, the application also uses Unity. I don't think it be the problem here.
The HttpGet Passed model is the same as HttpPost : DataVm
The name of the action is also the same : ProcessEnteredData
=================== Controller and action
[HttpPost]
public ActionResult ProcessEnteredData(DataVm vm)
{
if (ModelState.IsValid)
{
foreach (NumType num in vm.EnteredNums)
{
int i1 = num.Score1;
int i2 = num.Score2;
string profTitle = vm.Profile.Title;
Repository.Context.EnteredNums.Add(num);
}
return RedirectToAction("ShowTable");
}
else
{
return View(vm);
}
}
==============
The Partial View of Custom Editor Template are similar :
#model xxxx.NumType
#Html.LabelFor(m => m.TheTitle)
#Html.TextBoxFor(m => m.Score1)
#Html.TextBoxFor(m => m.Score2)
#Html.HiddenFor(m => m.Profile)
// Profile or ProfileId - Just used to see could it bring the property back or not as a test
============
NumType Model
[Key]
public int NumTypeId { get; set; }
[ForeignKey("Profile")]
[Required]
public int ProfileId { get; set; }
public int Score1 { get; set; }
public int Score2 { get; set; }
public int BoxId { get; set; }
public Box Box { get; set; } // something not important here
public virtual Profile Profile { get; set; }
============
The ViewModel
public class DataVm
{
public Profile Profile { get; set; }
public string TheTitle { get; set; }
public List<NumType> EnteredNums { get; set; }
// In the Editor template approach it's the only item with data and others are null
public List<Box> Boxes { get; set; }
}
=========
View for PartialView approach :
#model xxxx.DataVm
#using (Html.BeginForm("ProcessEnteredData", "Profile", FormMethod.Post))
{
#Html.AntiForgeryToken()
Model.EnteredNums = new List<NumType>();
foreach(var box in Model.Boxes)
{
NumType num = new NumType();
num.Profile = Model.Profile;
num.Box = box;
int iCount = Model.EnteredNums.Count;
Model.EnteredNums.Add(num);
#Html.Partial("NumView", Model.EnteredNums[iCount]);
}
<input type="submit" value="Do Process" />
}
===================
View for Editor for approach :
// instead of #Html.Partial :
#Html.EditorFor(m => m.EnteredNums[iCount]);
My question is related to this question and answer
The following complex model:
public class EditSubmissionModel
{
public string foo { get; set; }
public Submission submission { get; set; }
}
The simple model
[Table(Name = "Submission")]
public class Submission
{
[Column(IsPrimaryKey = true, IsDbGenerated = true, AutoSync = AutoSync.OnInsert)]
public int SubmissionId { get; set; }
[Column]
public string Title { get; set; }
}
The view:
#model Project.WebUI.Models.EditSubmissionModel
#{
ViewBag.Title = "editSubmission";
}
<h2>editSubmission</h2>
#using (Html.BeginForm())
{
<legend>SubmissionModel</legend>
#Html.EditorFor(m => m.foo)
#Html.EditorFor(m => m.submission)
<input type="submit" value="Save" />
}
the editorTemplate
#model Project.Domain.Entities.Submission
#Html.EditorFor(m => m.Title)
the controller
[Authorize]
[HttpPost]
public ActionResult editSubmission(string shortName, EditSubmissionModel model)
{
shortname = "second" (is ok)
model.foo = aaa (also ok i edited it on the view)
model.submission = null (not binded? or i dont know?)
I can't see the error, any ideas?
Status no repro. Steps:
Create a new ASP.NET MVC 3 application using the default template
Define 2 models:
public class Submission
{
public int SubmissionId { get; set; }
public string Title { get; set; }
}
public class EditSubmissionModel
{
public string foo { get; set; }
public Submission submission { get; set; }
}
Modify HomeController so that it looks like this:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(string shortName, EditSubmissionModel model)
{
return Content(model.submission.Title);
}
}
Update ~/Views/Home/Index.cshtml view so that it looks like this:
#model EditSubmissionModel
#using (Html.BeginForm())
{
<legend>SubmissionModel</legend>
#Html.EditorFor(m => m.foo)
#Html.EditorFor(m => m.submission)
<input type="submit" value="Save" />
}
Add a custom editor template for the Submission type (~/Views/Home/EditorTemplates/.cshtml) like this:
#model Submission
#Html.EditorFor(m => m.Title)
Hit Ctrl+F5, fill in the form and submit. As totally expected the value you have entered in the Title textbox will be correctly bound and shown on the screen.
So I repeat the question that I've already asked you in the comments section: what did you do differently? You answered that it is a copy-paste from your code, but as I have illustrated you (with a full step-by-step guide) this is not the case.
Now here's a suspicion that I have. Your actual POST action looks like this:
public ActionResult editSubmission(string shortName, EditSubmissionModel submission)
and not like this:
public ActionResult editSubmission(string shortName, EditSubmissionModel model)
Notice the parameter name.
#Darin Dimitrov you were completely right, what do i do different. The code above was completely fine. The problem was the get command which looked like:
[Authorize]
public ActionResult editSubmission(string confShortName, string submission)
{
//do stuff
return View();
}
And the Modelbinder will get problems if the httpPost anywhere has same name like the HttpGet in my case string submission and Editsubmission.submission. Big thanks to your detailed step by step advice!