MVC 3 Razor view BeginForm is not posting to controller in FF, but works in IE 9 - asp.net-mvc-3

I have a form in MVC 3 razor view that I am trying to post to my controller.
I need these:
1) Post the form to the controller action.
2) The action should do something with the data &return a string status (OK if success or NOK if failed)
3) Based on the result I might redirect the user after a brief delay.
4) I also want to prevent duplicate submission (if possible)
This is how my view looks (I trimmed it):
#model <MyNameSpace.Model>
#{
ViewBag.Title = "Save";
Layout = "~/Views/Shared/MyMaster.cshtml";
}
#using (Html.BeginForm("save", "my_controller"))
{
<div>
#Html.TextBoxFor(m => m.Host, new { #style = "width: 520px" })
... set other fields on the form ...
<input type="submit" id="btnSubmit" value="Submit"/>
</div>
}
This is my controller:
public String Save(<ModelName> model)
{
return "OK";
}
This seems working in IE9. But nothing happens in FF 4 or Opera. HttpFox shows no activity.
What is missing?
Thanks

In ASP.NET MVC it is considered good practice to have your controller actions return ActionResults instead of strings. This way proper content type headers will be set, etc...
So for example:
[HttpPost]
public ActionResult Save(ModelName model)
{
return Content("OK", "text/plain");
}
or if you wanted to return some view:
[HttpPost]
public ActionResult Save(ModelName model)
{
return View("Success");
}

Your example looks kosher, so either something critical is missing from your example code, or you need to view the generated HTML to see what's missing.

Did you omit the code that actually displayed the view to the user? I'm unsure of how this would function without a bit more. I mocked up your code and put in what I considered correct.
[HttpGet]
public ViewResult Save()
{
var vm = new ModelTest();
return View(vm);
}
[HttpPost]
public ActionResult Save(ModelTest model)
{
//do stuff with model
//Set a value in TempData -- Meets requirement of storing a status
TempData["Message"] = "OK";
//RedirectToRoute -- Meets requirement of preventing multiple posts partially. Some javascript will also help with this
RedirectToRoute("routename");
}
Post happened as expected in FF 4.01/5.0
Your model looks ok, it's just the controller code appears lacking.
Hopefully this helps.

Related

MVC Preserving Model When Jumping Between Controller and Forms

so I recently started project in .net MVC and have been having issues preserving data. I read somewhere that in order to do so, you have to pass the model back and forth. I tried doing this, but it still runs into issues. In my project, I have two buttons right now, getData and getData2. My view prints their true/false values. When I press one, it turns true, but if I press the other, it turns true but the other one goes to false. I want them both to stay true if I press them both.
Controller:
[HttpPost]
public ActionResult GetData(TestSite.Models.FarModels theFars)
{
theFars.HasData = true;
return RedirectToAction("FarCalc",theFars);
}
[HttpPost]
public ActionResult GetData2(TestSite.Models.FarModels theFars)
{
theFars.HasData2 = true;
return RedirectToAction("FarCalc", theFars);
}
[HttpGet]
public ActionResult FarCalc(TestSite.Models.FarModels theFars)
{
return View(theFars);
}
View:
#using (Html.BeginForm("GetData", "Home", FormMethod.Post))
{
//#Html.TextBoxFor(model => model.FarValue)
<input type="submit" value="GetData" />
}
#using (Html.BeginForm("GetData2", "Home", FormMethod.Post))
{
//#Html.TextBoxFor(model => model.FarValue)
<input type="submit" value="GetData2" />
}
#Model.HasData
#Model.HasData2
Thanks
You would need to make use of TempData object because the use of RedirectToAction returns a status code of 302, and the model binding does not exist. A good example is below.
https://www.codeproject.com/Tips/842080/How-to-Persist-Data-with-TempData-in-MVC

mvc controller returning raw html instead of the normal view

Last Edit
After hours spent googling, it turns out that bootstrap tab and server-validation do not play along. Thank you guys for the help.
Edit
The culprit is the repopulation of the selectlistitem. I am presented with a catch-22:
- remove the repopulation of the selectlistitem == error in view (null reference).
- add the repopulation of the selectlistitem == raw html.
Have anybody experience this issue?
I am experiencing something weird, I think. My controller is returning a raw html instead of a normal view when !modelstate.isvalid.
The GET:
[HttpGet]
public ActionResult AddNewProduct()
{
...
return View(productVM);
}
The Post:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddNewProduct(ProductVM prodVM, IEnumerable<HttpPostedFileBase> images)
{
if (ModelState.IsValid)
{
...
}
else
{
//repopulate dropdownlistfor
prodVM.slCategory = GetCategory(prodVM.prodDetailVM.storeID);
prodVM.selectedCat = 0;
prodVM.slAuctionType = GetAuctionType();
return View(prodVM);
}
}
The dropdownlist is a cascading type.
Can anybody give me a hint as to what is wrong? I have googled with no success. Thx.
Edit
I am not sure if the View is the problem, it renders ok on load, but displaying raw html on model not valid.

Partial View HttpPost invoked instead of HttpGet

I'm working on the admin part of an MVC webapp. I had the idea to use "widgets" for a single Admin panel. I'll explain my intentions first.
I have a languages table, and for that I'd like to create a partial view with a dropdownlist for those languages and a single button "Edit", that would take the user to a non-partial view to edit the language. After clicking save, the users would be redirected to the Index view, which would just show the dropdownlist again.
So I have a "Index.cshmtl", and an "EditLanguage.cshtml" as non-partial views, and a "LanguageWidget.cshtml" as a partial view.
First the user sees the Index view.
public ViewResult Index()
{
return View();
}
This view has the following code in it:
#using CodeBox.Domain.Concrete.ORM
#{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Administration</h2>
#Html.Action("LanguageWidget")
The Partial view "LanguageWidget" just contains the following code, and when the user submits it posts to the HttpPost annotated method in my controller:
#using (Html.BeginForm("LanguageWidget", "Admin"))
{
#Html.DropDownListFor(model => model.SelectedItem, Model.Languages)
<input type="submit" value="Edit"/>
}
This is the HttpPost method for the widget:
[HttpPost]
public ActionResult LanguageWidget(LanguageWidgetModel model)
{
var lang = langRepo.Languages.FirstOrDefault(l => l.LanguageId == model.SelectedItem);
return View("EditLanguage", lang);
}
This takes the user to the language edit page, which works fine.
But then! The user edits the language and submits the page, which invokes the "EditLanguage" HttpPost method, so the language is saved properly.
[HttpPost]
public ViewResult EditLanguage(Language model)
{
if (ModelState.IsValid)
{
langRepo.SaveLanguage(model);
TempData["message"] = string.Format("{0} has been saved!", model.Name);
return View("Index");
}
else
{
return View(model);
}
}
So, when I return the "Index" view - which seems logical I guess - the controller still assumes this is a HttpPost request, and when it renders the Index view, it invokes the "LanguageWidget" method, assuming it has to render the HttpPost method.
This leads to the LanguageWidget HttpPost method, which returns a full view with layout, returning just that, so I have my layout, with view, containing a layout, with the editview.
I don't really see how I could fix this?
I'm pretty sure it's a design flaw from my part, but I can't figure it out.
Thanks in advance!!
Consider using:
return RedirectToAction("Index")
instead of:
return View("Index");
It might seem more logical if the user is actually redirected to Index instead of
remaining at the EditLanguage. And if the user hits the refresh button no data will be resent using this approach.

ASP.NET MVC 3 Ajax.BeginForm and Html.TextBoxFor does not reflect changes done on the server

I'm using the Ajax.BeginForm helper in ASP.NET MVC3 to submit a form that replaces itself with new values in the form set on the server. However when I use helpers like Html.TextBoxFor I get the values that was submitted, not the values I inserted into the model on the server.
For example; I set SomeValue to 4 in my action and show it in a textbox. I change the value to 8, hit submit and would expect the value to be changed back to 4 in the textbox, but for some reason it stays 8. But if I output SomeValue without using Html helpers it says 4. Anybody have some clue about what is going on?
My controller:
public ActionResult Index(HomeModel model)
{
model.SomeValue = 4;
if (Request.IsAjaxRequest())
return PartialView(model);
return View(model);
}
public class HomeModel
{
public int? SomeValue { get; set; }
}
My View (please not that I have all the needed javascript in my layout page):
<div id="ajaxtest">
#using(Ajax.BeginForm(new AjaxOptions{ InsertionMode = InsertionMode.Replace,
UpdateTargetId = "ajaxtest", HttpMethod = "Post" })) {
#Html.TextBoxFor(model => model.SomeValue)
<input type="submit" value="Update" />
}
</div>
you can use
ModelState.Clear()
in your controller method to make the html helpers use your changed model. Otherwise they use the values from the form submit
Have a look at: Asp.net MVC ModelState.Clear
in your POST method you need to do
ModelState.Clear();
to reflect the changes made after the post

server side validation in the main view cause also validation in partial view, how to stop that?

I have layout with login partial view (username, password and submit button) and models ( some controls and submit button)with validation(server side and client side) displayed in normal views (#RenderBody() in layout).
My problem is when do server side validation in any of my views it also validate the login partial view because it execute the httppost function of the login. how can I stop that??
login view controller
[HttpGet]
public ActionResult LogOn()
{
return PartialView();
}
//
// POST: /Account/LogOn
[HttpPut]
public ActionResult LogOn(LogOnModel model)
{
if (ModelState.IsValid)
{
if (MembershipService.ValidateUser(model.UserName, model.Password))
{
FormsService.SignIn(model.UserName, model.RememberMe);
ViewBag.UserName = model.UserName;
}
else
{
ModelState.AddModelError("", Resources.Account.Account.LoginFailureText);
}
}
return PartialView(model);
}
and model controller
public ActionResult MyModel()
{
ViewBag.DisplayThxMsg = false;
return View();
}
[HttpPost]
public ActionResult MyModel(Models.FeedbacksModel feedback)
{
if (ModelState.IsValid)
{
//do something
}
else{
//do another thing
}
return View(feedback);
}
I find your question very difficult to understand. Im guessing your problem is you have a login partial control displayed as part of site layout and is shown on all pages. So while submitting any page, the username password validation kicks in, and you want to prevent that.
Understand that all validation # server - side happens while model binding, As properties are bound to the posted fields, the attributes on the fields are looked at and honored / catered to. So to prevent server side validation simply put the login partial view in it's own form so it is not sent while submitting other forms on the page.
In short have 2 forms - one form for login and one for feedback. Don't put all input fields in the same form.
If you still have validation errors after that, then it is because of other reasons like, type conversion problems. The default model binder will add some errors for basic type conversion issues (for example, passing a non-number for something which is an "int").The sample DataAnnotations model binder will fill model state with validation errors taken from the DataAnnotations attributes on your model.
EDIT
If you look at line number 125
#using (Html.BeginForm()){Html.RenderAction("LogOn", "Account");}
You have the above code which will render the login form.
It will do so inside the other form at line 45
<form id="form1" runat="server" method="post">
This has no end tag therefore it will encompass the whole document till </html>
You should change the structure from
<form id="form1" runat="server" method="post">
#using (Html.BeginForm()){Html.RenderAction("LogOn", "Account");}
</form
to
<form id="form1" runat="server" method="post">
</form>
#using (Html.BeginForm()){Html.RenderAction("LogOn", "Account");}
This line #using (Html.BeginForm()){Html.RenderAction("LogOn", "Account");} will render this form <form id="LoginView1" action="LogOn"> and all child elements of it.
LATEST EDIT
In your layout page use this :
#Html.Partial("~/Views/Shared/LogOnPartial.cshtml", new LogOnModel())
instead of this :
#Html.Action("LogOnPartial", "Account")
The reason why it all works is, the LogOnPartial method marked with [HttpPost] is called because the request was in a POST context. What you want is, You just need the view without the action executing even when POSTing. The above code does that. It renders the view without calling the action method. MVC3 is sort of a stupid servent : It only knows that it should call the Action method marked with [HttpPost] when the request is in a post context. It doesn't know that the request is in a post context for another action (index) and not this one (logonpartial). So now you can remove this method
public ActionResult LogOnPartial()
{
return PartialView();
}
which will no longer be used.
Note that you need to change the account controller's LogOnPartial Method to return
return RedirectToAction("Index","Home"); instead of return PartialView(model); on successful login. And on FAILURE you cannot render a partialview as you have coded. You must return an entirely new View. It must neither be index nor LogonPartails - just return return View("Login_Error_View"); which has its own layout. Otherwise it will be difficult to control the workflow.

Resources