I have the following model:
public class PersonListModel
{
....
[Required(ErrorMessage=AppConstants.MustSelectRecordToAttachMessage)]
public String SelectedPersonId { get; set; }
}
and the following view:
#using (Html.BeginForm("Attach", "Person", FormMethod.Post, new { #id = attachRecordFormId, targetDivId = personListId, #class = "inlineForm" }))
{
.....
#Html.HiddenFor(x => x.SelectedPersonId);
.....
<br />#Html.ValidationMessageFor(x => x.SelectedPersonId)
}
The hidden SelectedPersonId field is populated via some javascript hooked to the keyup event of one of the elements on my page.
My problem is that the required validation message shows immediately this partial view is displayed, not just when the form is submitted. It also displays again after the partial view is rendered again via an Ajax post.
I have very similar views that do not exhibit this problem, but 2 views (including the one above) that do exhibit this problem. I have been through a process of elimination to try to work out what is different between the views that work correctly and the 2 that exhibit this incorrect behavior, however I have not been able to locate the cause of the problem.
I presume that something is causing the unobtrusive validation to fire when the problem views are loaded. How can I track this down?
My problem is that the required validation message shows immediately this partial view is displayed
This could happen if the controller action that is displaying the view (containing the partial) takes the view model as argument:
public ActionResult Display(MyViewModel model)
{
... if this action is called with a GET request and you have missed
to pass a value for the "SelectedPersonId" query string parameter
you will get a validation error in the corresponding view
return View(model);
}
The reason for this happening is because your action is taking a model => the default model binder is kicking in attempting to populate your view model and when it attempts to set a value for the SelectedPersonId property it will automatically add a validation error to the model state if there's no corresponding value in the request because your model property is decorated with the [Required] attribute.
It also displays again after the partial view is rendered again via an Ajax post.
That's normal and could happen if the target POST action is taking your view model as argument and rendering a partial:
[HttpPost]
public ActionResult Process(MyViewModel model)
{
... if this action is called with a POST request and you have missed
to pass a value for the "SelectedPersonId" form parameter
you will get a validation error in the corresponding partial view
return PartialView(model);
}
Related
I have some strange errors in displaying model. I am creating something like chat. Users sending messages between yourself. When user selected one message in his inbox, and has clicked on the button "Answer", for example, form send submit(). Then information about selected message is displaying.
And in this moment I have a problem. If fields, that's displaying message properties are #Html.DisplayFor(), all works fine. Info of the message refreshed with change of selected message. But if properties displaying with #Html.TextAreaFor or Html.TextBoxFor it didn't happend with changing selected message.
And, if user clicked on "view" button, which display model in #Html.DisplayFor()
model displaying refresh, and refresh in #Html.DisplayFor() many many times. And as soon as click on "Answer" button, ie dipaly model in #Html.TextBoxFor(), model stopped to refresh display on changing selected message.
I drawed an image, for easier understanding. :)
Many Many Thanks!
I guess you are modifying the value you have bound the TextBoxFor on the HttpPost action:
[HttpPost]
public ActionResult SomeAction(MyViewModel model)
{
model.SomeProperty = "some new value";
return View(model);
}
and in the view you have:
#Html.TextBoxFor(x => x.SomeProperty)
If so, then this is by design. The HTML input helpers (such as TextBoxFor, TextAreaFor, CheckBoxFor, ...) are first looking in the ModelState when rendering their values and only after that in the model itself. So if you intend to modify some of the model properties in your POST action make sure that you remove it from the ModelState:
[HttpPost]
public ActionResult SomeAction(MyViewModel model)
{
ModelState.Remove("SomeProperty");
model.SomeProperty = "some new value";
return View(model);
}
Overview of the problem:
I've created a Surface controller with an action that is called using #Html.Action(...).
The #Html.Action call is done within a Macro partial view and the macro is included within the content of a page using the rich text editor.
(I'm new to this so if i'm going about things the wrong way then please let me know.)
The Surface controller has a GET and a POST action but it's the get action called within the macro partial.
Get action renders fine, entering no data into the form will invalidate the model state (which is what i'm currently testing).
submitting the form (with no entered data) means i can step into my POST action, ModelState.IsValid is set to false and CurrentUmbracoPage() is returned.
All fine... No Exceptions encountered when debugging...
It's at this point that the error text "Error loading Partial View script" appears on the page.
All I'm trying to do is return the same page with the validation messages showing.
Details:
Umbraco v6.0.5
The Controller I'm currently working on is used to reset a user's password. I also have a login conroller that is getting around this issue by using RedirectToCurrentUmbracoPage().
to access the page that contains the macro i use the address http://{testhost}/Reset-Password
the error text returned reads: Error loading Partial View script (file: ~/Views/MacroPartials/ResetPassword.cshtml)
code is within a seperate solution and views and bin directories are copied accross.
nuget package UmbracoCMS.Scaffolding is used.
Controller code:
public class ResetPasswordSurfaceController : SurfaceController {
[ChildActionOnly]
[HttpGet]
public ActionResult Reset(string token, string email) {
// Validation Code Omited
var user = Membership.GetUser(username);
return PartialView("Reset", new ResetPasswordSurfaceModel { UserID = user.ProviderUserKey.AsInt() });
}
[HttpPost]
public ActionResult PostReset(ResetPasswordSurfaceModel model) {
if (ModelState.IsValid) {
//Password reset code omited
return RedirectToCurrentUmbracoPage();
}
//works but only partial view content is rendered
// return PartialView("Reset",model);
return CurrentUmbracoPage();
}
}
View - ~\Views\ResetPasswordSurface\Reset.cshtml:
#model UmbracoExt.Models.ResetPasswordSurfaceModel
#using (Html.BeginUmbracoForm("PostReset", "ResetPasswordSurface")) {
#Html.EditorForModel()
<input type="submit" value="Submit" />
}
Macro Partial View - ~\Views\MacroPartials\ResetPassword.cshtml:
#inherits Umbraco.Web.Macros.PartialViewMacroPage
#Html.Action("Reset", "ResetPasswordSurface")
Any help is appreciated.
Edit:
Removing the [HttpGet] attribute from the Reset Action has revealed that after the PostReset action is called the Reset action is also called.
Renaming PostReset to Reset and re-adding the httpget attribute to the original Reset Action results in the post action being called twice.
the second time it is called causes the exception:
Can only use UmbracoPageResult in the context of an Http POST when using a SurfaceController form
I have reverted the changes so i'm back at Reset ([HttpGet]) being called after the PostReset action.
So the problem still stands. How can i get around this issue?
I need to return the result from the PostReset Action.
This is how I solved this problem:
I created extension method for model:
public static class ExtensionMethods
{
public static void MapModel<T>(this WebViewPage<T> page) where T : class
{
var models = page.ViewContext.TempData.Where(item => item.Value is T);
if (models.Any())
{
page.ViewData.Model = (T)models.First().Value;
page.ViewContext.TempData.Remove(models.First().Key);
}
}
}
Controller code:
[HttpPost]
public ActionResult Index(MyModel model)
{
TempData.Add("MyModel", model);
return RedirectToCurrentUmbracoPage();
}
Partial view code:
#using UmbracoTest.Extension
#using UmbracoTest.Models
#model MyModel
#{
this.MapModel<MyModel>();
}
#using (Html.BeginUmbracoForm("Index", "Home", FormMethod.Post))
{
<div>
#Html.TextBox("Text", Model.Text )
</div>
<input type="submit" name="submit" value="Submit" />
}
The Answers were given to me here
All credit goes to Shannon Deminick
The post action does not return anything for the response (that bit was new to me).
After the post when the Reset action is run the second time, since the modelstate is maintained, by passing a newly instantiated model, this model will inherit the model state of the model processed in the POST action (PostReset).
During the second time the Reset action was called, the validation logic meant it never gets to the point where it returns the partial view.
i temporarily bypassed the validation logic and sure enough the model validation messages were displayed.
I fixed this error by resolving a naming conflict:
Make sure that the GET and POST methods are named differently
Make sure the controller name doesn't conflict with any document types
I have a model with various properties but the one of interest is a List of another type of Model.
For example:
public class User
{
public string Name { get; set; }
public string Description { get; set; }
public IEnumerable<UserInterest> Interests { get; set; }
}
I then use an Editor Template within my view to render out a view for each item of the model items.
#Html.EditorFor(x => x.Interests)
The EditorFor template looks something like:
#model Interest
<div>
#Html.HiddenFor(x => x.Id)
#Html.TextBoxFor(x => x.InterestText)
#Html.CheckBoxFor(x => x.Delete)
....
</div>
Something very similar to the accepted answer here: Model Containing List of Models (MVC-3, Razor)
My question is - how would you from the client-side (jQuery) create a new item within the property without going back to the server. I currently have a rough way of doing it whereby I post the data back to my controller which returns the model back with a new blank item within the Interests property.
This seems to be overkill making a HTTP request and not very elegent. I was thinking of using jQuery .Clone() but not entirely sure on what I'd need to do in terms of naming the elements and clearing existing values.
So does anybody have any suggestions. I'm hoping to get more opinions and different approaches.
You can simply create the Textbox and checkbox on the fly and add that to the DOM. When saving it, Use jQuery ajax to post that data ( new record data) to an action method and save it there. Return a status back (Succcess /Falied) from your action method to your client side code ( your callback function of jQuery ajax/post) and check it there. If it is success, Show a success message to the user and append the new item to the existing list.
Sample jSFiddle : http://jsfiddle.net/carwB/2/
If you want to return some complex data ( ex : All new records with its id etc..) along with the status, you may return JSON from your action method.
EDIT : To keep your Model binding works with the newly added dynamic elements, you need to follow the naming convention of the elements.
The trick is to keep the id property value of the html element in this format.
CollectionName_ItemIndex__PropertyName
and name property value in this format
CollectionName[ItemIndex].PropertyName
I created a sample working program and explained it how it works Here based on your requirements.
In such situations I prefer to use client templating. You send data to server with ajax and then receive JsonResult. Look at JsRender this is javascript lib without jQuery dependency.
1.Create two partial view one is for list item and second one is creation
2.First partail view should be inside the div which has id 'divMdeolList'
3.and Creation view will have the code like that
#using (Ajax.BeginForm("SubmitData", new AjaxOptions { UpdateTargetId = "divMdeolList" }))
{
#Html.TextBoxFor(x => x.InterestText)
<p>
<input type="submit" value="Create" />
</p>
}
4. And then create a ActionResult type action on controller that will render the partialview
public ActionResult SubmitData(YourModel model)
{
//Do : save the record
return PartialView("FirstPartailView", model);
}
This will update the View without postback
I have an get action, which has a couple of parameters. In view I have a form, that fills after get action. Action creates an instance of model using received parameters, but if parameter has special value then action set to model some default values despite parameters. After that action return view with model.
The problem is that after changing the model in View shows data of the parameters, and not from the modified model.
This happens because, by design, all HTML helpers first look at the ModelState when binding and after that in the model itself. So if you intend to modify some value that was part of the POST request you will need to remove it from ModelState first if you want this change to be reflected in the view:
[HttpPost]
public ActionResult SomeAction(MyViewModel model)
{
// We remove the Bar property that was part of the request because
// we modify its value here
ModelState.Remove("Bar");
model.Bar = "Some modified value";
return View(model);
}
This assumes that in the corresponding view you have an input field for it:
#Html.EditorFor(x => x.Bar)
Update following initial comments
The model has an object in it called 'Account', of which there is an int propety (Account.AccountID)
ViewB has a form which collects some additional information - but also has a textbox which is populated with Model.Account.AccountID.
When I submit ViewB however, Model.Account becomes null.
Its probably easier to show a simplified version of what I have before I explain the issue:
[HttpGet]
public ActionResult ViewA()
{
return View(new BlahModel());
}
[HttpPost]
public ActionResult ViewA(BlahModel model)
{
if(there_was_a_problem)
return View("ViewA", model);
else
return View("ViewB", model);
}
// have tried both httppost, httpget and no attribute here
public ActionResult ViewB(BlahModel model)
{
return View(model);
}
I load up ViewA via a GET, fill in the strongly-typed form and submit - then the following View (either ViewA again or ViewB if the request had no problems) is fine ... it has full access to the whole model and can display the properties within it.
The problem is that if I then submit a form in ViewB (which posts to the ActionResult ViewB) - the model suddenly has null properties throughout, even though its using the same model - and prior to the post has picked up all the values sucessfully.
Any ideas?
Many thanks
Most likely - ViewB view does not render enough and model binder can't find values to bind.
Action argument is binded from form values. It does not matter if You pass model to view correctly. Rendering things out is what matters (or passing through query string/cookies).
Complementing Arnis L.'s answer, you can use a tool like Firebug to check that your model's parameters (or any parameters at all) are being sent along the request.