BeginForm<TController> won't post model first time round - asp.net-mvc

I have an issue using the latest mvc futures BeginForm extension. I have disabled clientsidevalidation as I have read that there may still be a bug with it. Basically the first time it gets posted the model is null, the second time (and any time after that) it will post the model fine.
It works if I switch it back to #using(Html.BeginForm()) or #using(Html.BeginForm("action", "controller" etc but I would rather make use of the strongly typed version. Here is my code:
Controller
[HandleError]
public class HomeController : BaseServiceController<IUserService>
{
public HomeController(IUserService service, IMapper mapper, ICustomPrincipal user)
: base(service, mapper, user)
{}
[AllowAnonymous]
[HttpGet]
public ActionResult Logon(string ReturnUrl)
{
LogonModel model = new LogonModel() { ReturnUrl = ReturnUrl };
return View(model);
}
[AllowAnonymous]
[HttpPost]
public ActionResult Logon(LogonModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
SfUser sfUser = service.Logon(model.UserName, model.Password);
if (sfUser == null)
{
ModelState.AddModelError("General", "Username or Password incorrect");
return View(model);
}
Response.Cookies.Add(TicketMaster.setAuthCookie(new CustomPrincipalSerializeModel(sfUser)));
return Redirect(model.ReturnUrl);
}
View
#model LogonModel
#{
ViewBag.Title = "Logon";
}
//#using(Html.BeginForm()) //Works
#using(Html.BeginForm<HomeController>(c => c.Logon(Model)))
{
#Html.HiddenFor(m => m.ReturnUrl)
<div class="editor-label">
#Html.LabelFor(m => m.UserName)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.Password)
</div>
<div class="editor-field">
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(m => m.Password)
</div>
<br />
<p>
<input type="submit" value="Login" /><br />
#Html.ValidationSummary(true)
</p>
}
I could understand the problem if it always posted a null model, but only on first post? Driving me mad.

I know this is old and probably already solved somehow, but you should pass null instead of Model to the expression. That way, the model will be built using the form values.
It took me a few hours to find this. Go to the Strong Typed Html BeginForm<TController> section. This is the key point:
You want to pass your values within the form’s scope, not the
BeginForm method itself.
In your case, just change
#using(Html.BeginForm<HomeController>(c => c.Logon(Model)))
to
#using(Html.BeginForm<HomeController>(c => c.Logon(null)))

Related

Passing data from a View to a Controller in .NET MVC - "#model" not highlighting

The following code works as I need it to:
#using (Html.BeginForm("LOLOL", "PATIENT", null))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>PATIENT</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</fieldset>
<p>
<input type="submit" value="SUBMIT" />
</p>
}
In LOLOLController:
[HttpPost]
public IActionResult LOLOL(Patient p) {
var client = new MongoClient("mongodb://localhost:27017");
var userId = _userManager.GetUserId(HttpContext.User);
string db_name = "test" + userId;
var database = client.GetDatabase(db_name);
var collection = database.GetCollection<BsonDocument>("patients");
var filter = Builders<BsonDocument>.Filter.Eq("Name", p.Name.ToString());
var document = collection.Find(filter).First();
// I'm cutting short the rest of the code, because when I do something
// similar later, "collection.Find(filter).First()" fires an exception, I'll
// explain..
return View(p);
}
I have something equivalent to taking off the fieldset element in the HTML, leaving basically just a button in the "Html.BeginForm", but then the data is clearly not binding properly, which I know because if I just have a button and no data-entry, I click the button and then I get an error saying the data cannot be found from the database. (EDIT: I now have confirmed that this is indeed because the Patient object is not being passed to the controller quite as I expected it to, seems like a brand new Patient object was created upon calling html.beginform ... I thought that maybe the old Patient object was being passed so I did not have to enter all its data members every time we use Html.BeginForm)
In sum, I want to fill out a text box, click a button to load a new page and display the value of that textbox, but have that value also persisted in essentially a session state, so that if I call another Html.BeginForm function and go into a third view, the text from the first view will be displayed in the third view, even though I did not have to type its value in the second view. Hopefully I can repeat this process, and essentially load up the data members of a class with one view per data member.
Make sure you pass the data from the previous view to the new view from your Controller. When you pass it, include #HiddenFor for those properties from the previous view in your new view. That way the new view will keep and then pass the values to your next POST.
#Html.HiddenFor(model => model.PropertyYouPassedAndWantToKeepAndPassAgain
Edit: Here's the logic for using multiple views for one object... as requested.
Model:
public class Patient
{
string Name { get; set; }
string Address { get; set; }
string City { get; set; }
}
Page1 GET:
[HttpGet]
public ActionResult Page1()
{
Patient patient = new Patient();
return View("~/Views/Page1.cshtml", patient);
}
Page 1 View... only ask for the name.
#model mysite.Models.Patient
#using (Html.BeginForm("LOLOL", "PATIENT", null))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>PATIENT</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</fieldset>
<p>
<input type="submit" value="SUBMIT" />
</p>
}
Page1 POST... get the patient and pass it on to the next view...
[HttpPost]
public ActionResult Page1(Patient patient)
{
if (ModelState.IsValid)
{
return View("~/Views/Page2.cshtml", patient); // pass your patient to the second page view with the name
}
else
{
return View("~/Views/Page1.cshtml", patient);
}
}
Page2 GET... get the patient from the prior Page1 POST and send it off to the Page2 View.
[HttpGet]
public ActionResult Page2(Patient patient)
{
// Receive patient from Page1 post and pass it to new view... includes the name
return View("~/Views/Page2.cshtml", patient);
}
Page2 View gets the object... use a HiddenFor to keep the name which you just sent from the GET.
#model mysite.Models.Patient
#using (Html.BeginForm("LOLOL", "PATIENT", null))
{
#Html.HiddenFor(model => model.Name) #* This will keep the name on your next post *#
#Html.ValidationSummary(true)
<fieldset>
<legend>PATIENT</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Address)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Address)
#Html.ValidationMessageFor(model => model.Address)
</div>
</fieldset>
<p>
<input type="submit" value="SUBMIT" />
</p>
}
Since the HiddenFor holds the Name, it will be passed on your next post. It is there but hidden from the form itself.
[HttpPost]
public ActionResult Page2(Patient patient)
{
// Because of the HiddenFor, the Name will be passed because it was kept in the view... but hidden from the form itself.
// It's basically storing it for you to pass again
if (ModelState.IsValid)
{
// Pass object with Name and Address to next controller
return View("~/Views/Page3.cshtml", patient);
}
else
{
return View("~/Views/Page2.cshtml", patient);
}
}
Page2 POST
[HttpPost]
public ActionResult Page2(Patient patient)
{
// Because of the HiddenFor, the Name will be passed because it was kept in the view... but hidden from the form itself.
// It's basically storing it for you to pass again
if (ModelState.IsValid)
{
// Pass object with Name and Address to next controller
return View("~/Views/Page3.cshtml", patient);
}
else
{
return View("~/Views/Page2.cshtml", patient);
}
}
Page3 GET
[HttpGet]
public ActionResult Page3(Patient patient)
{
// Pass patient again... to your next view
return View("~/Views/Page3.cshtml", patient);
}
Page3 View...
#using (Html.BeginForm("LOLOL", "PATIENT", null))
{
#Html.HiddenFor(model => model.Name) #* Keep name again for your next post *#
#Html.HiddenFor(model => model.Address) #* Now we are keeping the address as well *#
#Html.ValidationSummary(true)
<fieldset>
<legend>PATIENT</legend>
<div class="editor-label">
#Html.LabelFor(model => model.City)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.City)
#Html.ValidationMessageFor(model => model.City)
</div>
</fieldset>
<p>
<input type="submit" value="SUBMIT" />
</p>
}
And so on and so forth... until you have your model complete and want to do something with it.

Form submit using partail View error

I have created a form for add comment. I have created it in main View called Index in Home controller. bellow is Index View
private projectDBEntities db = new projectDBEntities();
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
public ActionResult AddComment()
{
return PartialView("_Comment");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddComment(comment cmt)
{
if (ModelState.IsValid)
{
db.comments.Add(cmt);
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
return PartialView("_Comment", cmt);
}
below is _Comment Partial View
#using (Html.BeginForm()) {#Html.AntiForgeryToken()#Html.ValidationSummary(true)
<fieldset>
<legend>comment</legend>
<div class="editor-label">
#Html.LabelFor(model => model.cmd_content)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.cmd_content)
#Html.ValidationMessageFor(model => model.cmd_content)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.t_email)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.t_email)
#Html.ValidationMessageFor(model => model.t_email)
</div>
<p>
<input type="submit" value="Comment" />
</p>
</fieldset>}
System.Web.HttpException was occuerd. I want to know What is the reason behind this error and what is the best method to form submit using partail View.
Id have to agree with Sandip's answer here, but to elaborate ..
#using (Html.BeginForm("AddComment", "Home"))
{
//Content to send to the server-side
}
As long as your model in your partial view points to your 'Comment' object, then this should work fine when using #Html.EditorFor(). In your controller, your already waiting for your 'Comment' object to be populated. So on Post, when the submit is clicked, it will populate that object.
Hope this helps.
#using (Html.BeginForm("AddComment", "Home"))
{
}

How do I call submit in mvc app?

I have mvc3 web app.
inside that I have one Enquiry form once I submit that form it should save all data into database for that I have used Entity Framework.
EnquiryController.cs
public class EnquiryController : Controller
{
aspnetdbEntities dbEntities = new aspnetdbEntities();
//
// GET: /Enquiry/
public ActionResult Index()
{
return View();
}
//
// GET: /Enquiry/
public ActionResult Enquiry()
{
return View();
}
//
//POST: /Enquiry/
[HttpPost]
public ActionResult Enquiry(Enquiry enquiry)
{
if (ModelState.IsValid)
{
dbEntities.Enquiries.AddObject(enquiry);
dbEntities.SaveChanges();
return RedirectToAction("Index");
}
return View(enquiry);
}
}
Enquiry.cshtml
#model MyWeb.Models.Enquiry
#{
ViewBag.Title = "Enquiry";
}
<h2>Enquiry</h2>
<div>
<fieldset>
<legend>Enquiry Form</legend>
<div class="editor-label">
#Html.LabelFor(m => m.FirstName)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.FirstName)
#Html.ValidationMessageFor(m => m.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(m => m.LastName)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => m.LastName)
#Html.ValidationMessageFor(m => m.LastName)
</div>
<p>
<input type="submit" value="Submit" />
</p>
</fieldset>
</div>
But when I clicked on Submit button it is not working no round rip no refresh
please help me where i go wrong i'm newbie.
ashuthinks,
just add the htmlHelper (for forms) to your view:
#using(Html.BeginForm()){
// exisiting fieldset stuff
<fieldset>... bla</fieldset>
}
around your fieldset and you're good to go

asp.net mvc scaffolding and GUID as the primary key

I have a table with the following structure
GUID uniqueidentifier with default value newid(), and set as primary key
ID int
Description varchar(max)
I created an Entity Model using visual studio, and generated the views for editing/deleting etc (mvc scaffolding)
The problem is with "Editing", when I click that link, the appropriate form is showed with the correct data, but the "save" button doesn't work at all. Please note that other links (delete,create,details) work perfectly..
So, when I click the "Edit" link, the url is
http://localhost:10871/admin/Edit/e7d0c5ee-7782-411f-920e-7b0d93c924e1
and the form is displayed correctly, but the save button doesn't work, no network activity happens. Is something particular about using uniqueidentifers as primary key?
Thanks
---Code---
Edit.cshtml
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Domain</legend>
#Html.HiddenFor(model => model.GUID)
#Html.HiddenFor(model => model.ID)
<div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
--AdminController.cs--
// GET: /Admin/Edit/5
public ActionResult Edit(Guid id)
{
Domain domain = db.Domains.Single(d => d.GUID == id);
return View(domain);
}
//
// POST: /Admin/Edit/5
[HttpPost]
public ActionResult Edit(Domain domain)
{
if (ModelState.IsValid)
{
db.Domains.Attach(domain);
db.ObjectStateManager.ChangeObjectState(domain, EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(domain);
}
Edit2
In response to a comment by ZippyV, I added the following code in Edit.cshtml
<div class="editor-label">
#Html.LabelFor(model => model.ID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ID)
#Html.ValidationMessageFor(model => model.ID)
</div>
to my surprise (or ignorance) - the GUID is being shown instead of ID
and apart from that - when I enter a value in that field (1,2 or any integer), I still get the message "The field ID must be a number."
The problem comes from the fact that you have a property called ID on your view model:
public class Domain
{
public int ID { get; set; }
...
}
and at the same time you have a route parameter called id in your Edit controller action:
public ActionResult Edit(Guid id)
{
...
}
Now here's what happens: The Html helpers (such as TextBoxFor, HiddenFor, ...) always first looks in the model state when binding a value (i.e. query string parameters, route values) and only at the end it looks at the value in the model. That's how all Html helpers work and it is by design.
So you have the following:
#Html.HiddenFor(model => model.ID)
The ID is taken NOT from the model but from your Route data (which is a Guid as specified in your action parameter). That's why it is very dangerous to use properties in the view model and action parameters that have the same name.
One possible workaround would be to rename your action parameter to something else:
public ActionResult Edit(Guid domainId)
{
Domain domain = db.Domains.Single(d => d.GUID == domainId);
return View(domain);
}
Of course now your routes might need to be adapted or use query string parameters: controller/edit?domainId=d578b4b7-48ec-4846-8a49-2120c17b6441 to invoke the Edit action.

keeping Object Data after passing it to a strongly typed view

I have a simple ASP.NET MVC 3 site, from my Controller out of the Edit Action I pass a Object (a class which is also mapped by nhibernate)
After editing and clicking save i pass it to the [HTTPPost] decoraded Method but and all properties are correct, excerpt the "id" property it hat a guid value equivalent to NULL (00000000-0000-000...).
Is there a problem using the Domain Model to strongly type my Views? May the problem be that Id has:
{get; private set;}
???
Thanks in advance.
Here The Code:
My View:
'#model fnh.DataModel.Kunde
#{
View.Title = "EditKunde";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>EditKunde</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Fields</legend>
#Html.EditorFor(model => model._id)
#Html.EditorFor(model => model._KdNr);
<div class="editor-label">
#Html.LabelFor(model => model._Name)
`enter code here` </div>
<div class="editor-field">
#Html.EditorFor(model => model._Name)
#Html.ValidationMessageFor(model => model._Name)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
'
My Controller Actions:
' public ActionResult EditKunde(Guid id)
{
return View(_kunden.GetKundeById(id));
}
[HttpPost]
public ActionResult EditKunde(Kunde kunde)
{
Ansprechpartner anp = new Ansprechpartner();
anp._Name = "JustATry";
kunde._Ansprechpartner.Add(anp);
`enter code here` _kunden.EditKunde(kunde);
return View();
}'
Are you adding the Id property to your form (maybe as a hidden field)?
It would be useful if you post the Action code (for both Edit Actions), and the View.

Resources