0

I'm using jquery popup overlay (http://dev.vast.com/jquery-popup-overlay/) to display the contents of the Credit action (shown below).

With the code shown below, I am expecting the fields in the popup to be cleared since I am returning the partial view with a newly created model with no values set; however the fields are not updated at all.

The crazy thing is, if I comment out the "return PartialView(model);" statement and uncomment the "return Json("Yay it worked!");" then the popup gets replaced with "Yay it worked!". (Yes, I also have to change the return type of the action when I uncomment that line).

Why would Ajax.BeginForm have no problem replacing the target div with the text of a Json return value, but totally ignore the results of a PartialViewResult?

public PartialViewResult Credit()
{
    CreditPaymentModel model = new CreditPaymentModel();
    return PartialView(model);
}

[HttpPost, ValidateAntiForgeryToken]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")]
public PartialViewResult Credit(CreditPaymentModel model) {

    model = new CreditPaymentModel(); // breakpoint here
    return PartialView(model);
    //return Json("Yay it worked!");
}

EDIT: I just added a function to process the OnSuccess event. I can step through the view as it's being processed (after submitting and it returns the view with an empty model) and verify that the values are in fact null, however when it gets to the OnSuccess method the data it receives shows the original data that was submitted rather than the blank values I'm expecting. Normally I would expect this to be due to caching, but I have an OutputCache attribute over the Credit action and I have set the AllowCache property to false in the AjaxOptions so I don't understand how this could be happening?!

BVernon
  • 3,205
  • 5
  • 28
  • 64

1 Answers1

1

The HtmlHelper methods your using in your partial view form use values from ModelState rather than from your model if they exist, which in your case they do because your POST method has a parameter which is typeof CreditPaymentModel and each value of CreditPaymentModel has been added to ModelState by the DefaultModelBinder.

You would need to use ModelState.Clear; before you return the model if you want to display the default values for CreditPaymentModel. For a more detailed explanation of the behavior, refer TextBoxFor displaying initial value, not the value updated from code.

Note however, because your returning a new partial, all client side validation will be lost unless you re-parse the $.validator (refer this answer for an example). All this will be easier is you use the $.ajax() method which gives far more flexibility, and in your case, have the method returns the partial is ModelState is invalid and you want to display validation errors, or return null otherwise. In the ajax success callback, if the result is null, just reset the existing form controls, otherwise replace the partial and re-parse the $.validator.

Community
  • 1
  • 1
  • Just to confirm I'm understanding you correctly, you're saying that when the "return PartialView(model);" statement is run it totally ignores the model I pass in and just uses the ModelState instead? If so that certainly explains the behavior I'm seeing, but it makes absolutely no sense to me why they would make it work that way. – BVernon Mar 17 '17 at 18:15
  • Okay, did some more reading and see that this is the "normal" behavior. Freaking unintuitive if you ask me though. Seems like it would make way more sense for it to actually honor the statement you made and provide an explicit method to have it use ModelState if you want to instead of trying to be smarter than you are and force you to explain "no, I really meant what I said" by having to clear the ModelState in order for it to do what you told it to do. Sigh... but whatever, that's Microsoft for you. – BVernon Mar 17 '17 at 18:26
  • Since the vast majority of posts involve `if(!ModelState.IsValid){ return View(model); } else { // save and redirect }` and that `return View()` means return **the** view, not another view (which you want), then I think the MVC made the correct decision (and in your case, how would you handle `ModelState` being invalid anyway?) –  Mar 17 '17 at 23:37
  • I'm not sure I entirely understand your question. You mention the majority of posts including "... return View(model); ...". If I understand correctly, the model passed in is completely ignored since it is the ModelState which is used. So it 'sounds' to me like your argument is that because most of the time the model is passed back into the view with whatever the user put on it (when there is a validation error), that it's therefore okay to ignore any changes the code makes to that model before passing it back into the view because that's not normal. Perhaps I am not understanding you though? – BVernon Mar 19 '17 at 06:11
  • The 2nd part of [this answer](http://stackoverflow.com/questions/26654862/textboxfor-displaying-initial-value-not-the-value-updated-from-code/26664111#26664111) explains it, and why the MVC team made the decision to use values from `ModelState` if they exist –  Mar 19 '17 at 06:21
  • Yeah I hear you. But if you or I presented something like that in code that our consumers had to use, how many people on this site do you think would say "yeah, that's a great idea!"? I would guarantee everyone would be shouting "What are you thinking?!" and they would provide much better alternatives that are intuitive (or at a minimum, not so unintuitive) and don't override the natural behavior you should be able to expect your code to produce. – BVernon Mar 19 '17 at 22:36
  • Well that's your opinion :) Personally I would be pretty confused if I entered some data and when submitting the form, that data was suddenly changed and it displayed an error message that made no sense. You cant have it both ways. –  Mar 19 '17 at 22:40
  • They absolutely could design it to have it both ways. Instead of hijacking the View(model); statement to replace model with something else they could have just as easily created a new method called something like ViewWithModelState() (of course it would have a better name... not going to spend 10 minutes thinking of a proper name here but you get the point). Or better yet there could be an overload that actually takes the ModelState as an argument... seems like that would make absolutely perfect sense. – BVernon Mar 19 '17 at 23:51
  • Don't get me wrong, I love Microsoft and all they've done with MVC. Doesn't mean I'm going to defend something as 'right' just because it's how they did it though. That's just silliness. – BVernon Mar 19 '17 at 23:52
  • You go talk to any skilled programmers who don't work with Microsoft and they will all say the implementation is poor. Only Microsoft programmers would ever defend that design. – BVernon Mar 19 '17 at 23:54
  • I don't work for MS, and I consider myself to be just a little more skilled than most in MVC, and I consider they made the right decision :) –  Mar 20 '17 at 00:00
  • Ah, I didn't mean programmers who work for Microsoft... meant programmers who work with Microsoft products. In other words, talk to people who work with every product under the sun that is not Microsoft and see who would say that was a good idea. Go to the Software Engineering site for example and post the question... I doubt you'll find many people who agree it was a good design decision. – BVernon Mar 20 '17 at 00:12